Django 简易PACS读片系统

1、Django中写一个后端接口,给HTML提供dicom文件接口的方式

1、首先创建django项目

1、下载安装跨域的包
pip3 install django-cors-headers
2、使用pycharm创建一个Django项目

请添加图片描述

3、点击创建在另一个窗口,这个都无所谓,怎么都行,就是打开这个项目
4、创建成功的项目就是下面这个样子

请添加图片描述

5、先修改几个选项,后面好操作

下面是我的settings设置,我把修改的地方标注出来

"""
Django settings for pacsdemo project.

Generated by 'django-admin startproject' using Django 3.2.18.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
# 添加这个import os
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-uv!_mka##8#citwx_q$o71)@!8tfaa-6wyrb!h8&4fm+m-=-d='

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# 设置所有IP都可以访问,局域网
ALLOWED_HOSTS = ['*']

# Application definition

# 添加跨域设置
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pacs.apps.PacsConfig',
    'corsheaders'  # 跨域设置
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# 新添加的设置
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = False
CORS_ORIGIN_WHITELIST = [
    "http://192.168.0.30:8000",
    "http://localhost:63409",
]
CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW'
)

CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)


ROOT_URLCONF = 'pacsdemo.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates']
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'pacsdemo.wsgi.application'

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

# 连接本地的mysql数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'pacsdemo',   #  你的数据库名称
        'USER': '******',  # 你的账户
        'PASSWORD': '******',  #你的密码
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

# 设置时间,上海的时间
LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

# 加载静态文件的设置
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

6、把表结构映射到数据库中

连接数据库的设置,没有这个设置,数据库会连接失败

import pymysql
pymysql.install_as_MySQLdb()

请添加图片描述

7、在终端运行两行命令
python manage.py makemigrations

请添加图片描述

8、运行第二行命令
python manage.py migrate

请添加图片描述

9、第一步完成,数据库连接成功,基础设置完成

2、写前端页面,并且和后端接口,传递单个dicom文件,并且显示出来

1、先添加一个路由

from django.conf.urls import include

请添加图片描述

2、然后在pacs中写urls.py里面的内容

from django.urls import path

from . import views

urlpatterns = [
    path("index/", views.index),  # 设置首页内容
    path("read/", views.read),  # 读dicom文件
    path("read1/", views.read1),  # 读dicom文件
]

请添加图片描述

3、写这个index的方法,在views.py中

from django.shortcuts import render
from django.http import HttpResponse, FileResponse


# Create your views here.
def index(request):
    return render(request, 'index.html')


def read(request):
    file = open('static/dicom/C9214289', 'rb')
    response = FileResponse(file)
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="1.dcm"'
    return response


def read1(request):
    file = open('static/dicom/C9214530', 'rb')
    response = FileResponse(file)
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="2.dcm"'
    return response


请添加图片描述

4、写index.html的内容

我写的这个html,直接放进去就可以看,其中的内容你可以通过cornerstone.js查看

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>pacs</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>


    <script type="text/javascript">

        // 阻止右键点击事件的默认行为
        document.addEventListener('contextmenu', function (event) {
            event.preventDefault();
        });
    </script>
    <script>
        // 监听双击事件,并阻止默认的缩放行为
        document.addEventListener('dblclick', function (event) {
            event.preventDefault();
        });
        document.addEventListener('touchstart', function (event) {
            if (event.touches.length > 1) {
                event.preventDefault();
            }
        }, {passive: false});
    </script>
</head>
<body>
<div>
    <h2>全部都是鼠标左键的操作</h2>
    <button id="wwwc">切换窗宽窗位</button>
    <button id="length">切换测量长度</button>
    <button id="rotate">旋转</button>
    <button id="magnify">透视</button>
    <button id="pan">移动</button>
    <br/>
    <button id="scaleoverlay">尺度</button>
    <button id="zoom">放大</button>
    <button id="angle">测量角度</button>
    <button id="arrowannotate">添加备注</button>
    <button id="bidirectional">交叉线</button>
    <br/>
    <button id="cobbangle">心胸比</button>
    <button id="ellipticalroi">圆</button>
    <button id="freehandroi">随便画</button>
    <button id="probe">探针</button>
    <button id="rectangleroi">方形</button>
    <br/>
    <button id="circlescissors">画圆</button>
    <button id="rectanglescissors">画正方形</button>
</div>
<div id="dicomImage" style="width: 512px;height: 512px;position: absolute"></div>

</body>
<!-- 触控的方法 -->
<script src="https://unpkg.com/hammerjs@2.0.8/hammer.js"></script>
<!-- 基石包,核心包 -->
<script src="https://unpkg.com/cornerstone-core@2.6.1/dist/cornerstone.js"></script>
<!-- Math包,数学包 -->
<script src="https://unpkg.com/cornerstone-math@0.1.10/dist/cornerstoneMath.min.js"></script>
<!-- 图片解析包,解析dcm文件的 -->
<script src="https://unpkg.com/cornerstone-wado-image-loader@4.13.2/dist/cornerstoneWADOImageLoader.bundle.min.js"></script>
<!-- 解析普通图的方法 -->
<script src="https://unpkg.com/cornerstone-web-image-loader@2.1.1/dist/cornerstoneWebImageLoader.min.js"></script>
<!-- 写好方法的包 -->
<script src="https://unpkg.com/cornerstone-tools@6.0.10/dist/cornerstoneTools.js"></script>
<!-- 解析dicom的json信息 -->
<script src="https://unpkg.com/dicom-parser@1.8.21/dist/dicomParser.min.js"></script>

<script>
    cornerstoneTools.external.cornerstone = cornerstone;
    cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
    cornerstoneTools.external.Hammer = Hammer;
    cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
    cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
    // 'dicomweb' 网页上的一种解析的包, http开头的网址 ,获取dicom的文件,并且显示出来
    var imageId = "wadouri:http://127.0.0.1:8000/read";
    var imageIds = ["wadouri:http://127.0.0.1:8000/read", "wadouri:http://127.0.0.1:8000/read1"]

    // 定义stack滚动工具
    const StackScrollMouseWheelTool = cornerstoneTools.StackScrollMouseWheelTool
    // Add our tool, and set it's mode
    const StackScrollTool = cornerstoneTools.StackScrollTool
    //define the stack
    // 设置一个栈
    const stack = {
        currentImageIdIndex: 0,
        imageIds
    }

    // 默认初始化tools
    cornerstoneTools.init()
    // 获取页面显示的dicom id
    var element = document.getElementById('dicomImage');
    cornerstone.enable(element)

    // 测量长度
    const LengthTool = cornerstoneTools.LengthTool;
    cornerstoneTools.addTool(LengthTool)
    // cornerstoneTools.setToolActive('Length', {mouseButtonMask: 1})
    // wwwc
    const WwwcTool = cornerstoneTools.WwwcTool;
    cornerstoneTools.addTool(WwwcTool)
    // cornerstoneTools.setToolActive('Wwwc', {mouseButtonMask: 1})

    // 旋转
    const RotateTool = cornerstoneTools.RotateTool;
    cornerstoneTools.addTool(RotateTool)
    // 透视
    const MagnifyTool = cornerstoneTools.MagnifyTool;
    cornerstoneTools.addTool(MagnifyTool)
    // 移动
    const PanTool = cornerstoneTools.PanTool;
    cornerstoneTools.addTool(PanTool)
    // 尺度
    const ScaleOverlayTool = cornerstoneTools.ScaleOverlayTool;
    cornerstoneTools.addTool(ScaleOverlayTool)
    // 放大
    const ZoomTool = cornerstoneTools.ZoomTool;
    cornerstoneTools.addTool(cornerstoneTools.ZoomTool, {
        // Optional configuration
        configuration: {
            invert: false,
            preventZoomOutsideImage: false,
            minScale: .1,
            maxScale: 20.0,
        }
    });
    // 测量角度
    const AngleTool = cornerstoneTools.AngleTool;
    cornerstoneTools.addTool(AngleTool)

    // 添加备注
    const ArrowAnnotateTool = cornerstoneTools.ArrowAnnotateTool;
    cornerstoneTools.addTool(ArrowAnnotateTool)
    // 交叉线
    const BidirectionalTool = cornerstoneTools.BidirectionalTool;
    cornerstoneTools.addTool(BidirectionalTool)
    // 心胸比
    const CobbAngleTool = cornerstoneTools.CobbAngleTool;
    cornerstoneTools.addTool(CobbAngleTool)
    // 圆
    const EllipticalRoiTool = cornerstoneTools.EllipticalRoiTool;
    cornerstoneTools.addTool(EllipticalRoiTool)
    // 画笔
    const FreehandRoiTool = cornerstoneTools.FreehandRoiTool;
    cornerstoneTools.addTool(FreehandRoiTool)
    // 探针
    const ProbeTool = cornerstoneTools.ProbeTool;
    cornerstoneTools.addTool(ProbeTool)
    // 正方形长方形
    const RectangleRoiTool = cornerstoneTools.RectangleRoiTool;
    cornerstoneTools.addTool(RectangleRoiTool)
    // 直接画圆
    const CircleScissorsTool = cornerstoneTools.CircleScissorsTool;
    cornerstoneTools.addTool(CircleScissorsTool)
    // 通过正方形涂
    const RectangleScissorsTool = cornerstoneTools.RectangleScissorsTool;

    cornerstoneTools.addTool(RectangleScissorsTool)


    // 鼠标中键滚动
    cornerstone.loadAndCacheImage(imageIds[0]).then(function (image) {
        cornerstone.displayImage(element, image)
        cornerstoneTools.addStackStateManager(element, ['stack'])
        cornerstoneTools.addToolState(element, 'stack', stack)
    })

    cornerstoneTools.addTool(StackScrollMouseWheelTool)
    cornerstoneTools.setToolActive('StackScrollMouseWheel', {})

    function disableAllTools() {
        // 取消左键的功能
        cornerstoneTools.setToolDisabled('Length')
        cornerstoneTools.setToolDisabled('Wwwc')
        cornerstoneTools.setToolDisabled('Rotate')
        cornerstoneTools.setToolDisabled('Magnify')
        cornerstoneTools.setToolDisabled('ScaleOverlay')
        cornerstoneTools.setToolDisabled('Zoom')
        cornerstoneTools.setToolDisabled('Angle')
        cornerstoneTools.setToolDisabled('ArrowAnnotate')
        cornerstoneTools.setToolDisabled('Pan')
        cornerstoneTools.setToolDisabled('Bidirectional')
        cornerstoneTools.setToolDisabled('CobbAngle')
        cornerstoneTools.setToolDisabled('EllipticalRoi')
        cornerstoneTools.setToolDisabled('FreehandRoi')
        cornerstoneTools.setToolDisabled('Probe')
        cornerstoneTools.setToolDisabled('RectangleRoi')
        cornerstoneTools.setToolDisabled('CircleScissors')
        cornerstoneTools.setToolDisabled('RectangleScissors')
    }

    document.getElementById("wwwc").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活窗宽窗位
        cornerstoneTools.setToolActive("Wwwc", {mouseButtonMask: 1})

    })
    document.getElementById("length").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活测量长度
        cornerstoneTools.setToolActive("Length", {mouseButtonMask: 1})

    })
    document.getElementById("rotate").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活旋转
        cornerstoneTools.setToolActive("Rotate", {mouseButtonMask: 1})

    })
    document.getElementById("magnify").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活透视
        cornerstoneTools.setToolActive("Magnify", {mouseButtonMask: 1})

    })
    document.getElementById("pan").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 移动
        cornerstoneTools.setToolActive("Pan", {mouseButtonMask: 1})

    })
    document.getElementById("scaleoverlay").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 尺子
        cornerstoneTools.setToolActive("ScaleOverlay", {mouseButtonMask: 1})

    })
    document.getElementById("zoom").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 放大
        cornerstoneTools.setToolActive("Zoom", {mouseButtonMask: 1})

    })
    document.getElementById("angle").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 角度
        cornerstoneTools.setToolActive("Angle", {mouseButtonMask: 1})

    })
    document.getElementById("arrowannotate").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 添加备注
        cornerstoneTools.setToolActive("ArrowAnnotate", {mouseButtonMask: 1})

    })
    document.getElementById("bidirectional").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活交叉线
        cornerstoneTools.setToolActive("Bidirectional", {mouseButtonMask: 1})

    })
    document.getElementById("cobbangle").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活心胸比
        cornerstoneTools.setToolActive("CobbAngle", {mouseButtonMask: 1})

    })
    document.getElementById("ellipticalroi").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 圆
        cornerstoneTools.setToolActive("EllipticalRoi", {mouseButtonMask: 1})

    })
    document.getElementById("freehandroi").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 随便画
        cornerstoneTools.setToolActive("FreehandRoi", {mouseButtonMask: 1})

    })
    document.getElementById("probe").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 探针
        cornerstoneTools.setToolActive("Probe", {mouseButtonMask: 1})

    })
    document.getElementById("rectangleroi").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 方形
        cornerstoneTools.setToolActive("RectangleRoi", {mouseButtonMask: 1})

    })
    document.getElementById("circlescissors").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 方形
        cornerstoneTools.setToolActive("CircleScissors", {mouseButtonMask: 1})

    })
    document.getElementById("rectanglescissors").addEventListener('click', function () {
        // 取消左键原来的功能
        disableAllTools();
        // 激活 方形
        cornerstoneTools.setToolActive("RectangleScissors", {mouseButtonMask: 1})

    })

</script>

</html>

5、启动项目

1、第一种,通过pycharm打开

请添加图片描述请添加图片描述

2、第二种,命令行的方式
python manage.py runserver

5、查看网页

其中的功能都可以进行操作,后续我进行优化,默认滚动条是切换dicom

http://127.0.0.1:8000/index/

请添加图片描述

6、成功