# Orthanc plugin for mammography # Copyright (C) 2024 Edouard Chatzopoulos and Sebastien Jodogne, # ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Affero General Public License # as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . ## ## Initialize the virtual environment for pytorch. ## DO NOT add other "import" in this section! ## import sys import json import orthanc config = json.loads(orthanc.GetConfiguration()).get('Mammography', {}) venv = config.get('VirtualEnv') if venv != None: # https://orthanc.uclouvain.be/book/plugins/python.html#working-with-virtual-environments sys.path.insert(0, venv) ## ## Install the Stone Web viewer ## STONE_VERSION = '2024-08-31-StoneWebViewer-DICOM-SR' VIEWER_PREFIX = '/mammography-viewer/' import os SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) VIEWER_DIR = os.path.join(SCRIPT_DIR, 'viewer') sys.path.append(os.path.join(SCRIPT_DIR, '..')) import download os.makedirs(VIEWER_DIR, exist_ok = True) download.get(os.path.join(VIEWER_DIR, '%s.zip' % STONE_VERSION), 'https://github.com/jodogne/orthanc-mammography/raw/master/viewer/%s.zip' % STONE_VERSION, 4815178, '86b52a17f86e4769d12e9ae680c4a99f') import zipfile stone_assets = zipfile.ZipFile(os.path.join(VIEWER_DIR, '%s.zip' % STONE_VERSION)) MIME_TYPES = { '.css' : 'text/css', '.gif' : 'image/gif', '.html' : 'text/html', '.jpeg' : 'image/jpeg', '.js' : 'text/javascript', '.png' : 'image/png', } def serve_stone_web_viewer(output, uri, **request): if not uri.startswith(VIEWER_PREFIX): output.SendHttpStatusCode(404) elif request['method'] != 'GET': output.SendMethodNotAllowed('GET') else: try: path = '%s/%s' % (STONE_VERSION, uri[len(VIEWER_PREFIX):]) extension = os.path.splitext(path) [1] if not extension in MIME_TYPES: mime = 'application/octet-stream' else: mime = MIME_TYPES[extension] with stone_assets.open(path) as f: output.AnswerBuffer(f.read(), mime) except: output.SendHttpStatusCode(500) orthanc.RegisterRestCallback('%s(.*)' % VIEWER_PREFIX, serve_stone_web_viewer) ## ## Load the deep learning model ## import highdicom import io import os import pydicom sys.path.append(os.path.join(SCRIPT_DIR, '..')) import model import dicom_sr import dicom_sr_to_pdf orthanc.LogWarning('Loading the RetinaNet model for mammography') my_retina_net = model.load_retina_net() ## ## Install the Orthanc Explorer extension ## with open(os.path.join(SCRIPT_DIR, 'OrthancExplorer.js'), 'r') as f: orthanc.ExtendOrthancExplorer(f.read()) def execute_inference(output, uri, **request): if request['method'] != 'POST': output.SendMethodNotAllowed('POST') else: body = json.loads(request['body']) f = orthanc.GetDicomForInstance(body['instance']) dicom = pydicom.dcmread(io.BytesIO(f)) if len(dicom.pixel_array.shape) != 2: orthanc.LogError('Not a graylevel instance: %s' % body['instance']) output.SendHttpStatusCode(400) else: result = dicom_sr.apply(my_retina_net, dicom, minimum_score=0.2) pdf = dicom_sr_to_pdf.create(dicom.pixel_array, result) with io.BytesIO() as f: pydicom.dcmwrite(f, result) f.seek(0) content = f.read() output.AnswerBuffer(orthanc.RestApiPost('/instances', content), 'application/json') with io.BytesIO() as f: pydicom.dcmwrite(f, pdf) f.seek(0) content = f.read() output.AnswerBuffer(orthanc.RestApiPost('/instances', content), 'application/json') orthanc.RegisterRestCallback('/mammography-apply', execute_inference)