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/