Is it possible to install Orthanc on a dedicated hosted server ? If so, which OS ?

56 views
Skip to first unread message

Stephen Douglas Scotti

unread,
Apr 26, 2020, 4:01:01 AM4/26/20
to Orthanc Users
We are exploring installation of an Orthanc Instance on a dedicated hosted server, for development purposes.  The service that we currently have usually runs CentOS with cPanel as an option, but they also have a Debian system.  Not sure if it is Ubuntu, but, if not, something similar.  With a dedicated server we would have SSH access and I think root privileges, and some level of support from the hosting service.


I actually found some instructions for CentOS install using source here, although not sure that that works, and if CentOS is not really recommended or supported by the Orthanc developers it would probably be better to use a Debian system.


I've been using the MacOS with Horos and a MAMP Pro package on a high-end iMac and that works almost out of the box.  Seems like installing on a hosted system could be problematic just because the system would not be on-site and if something needs attention you would have to rely on the hosting service or cPanel support to rectify things, although on a development server it is not as much of a problem as long as there is not too much down time.

Not sure if anyone on the forum has had experience with a hosted dedicated server.

I don't know if posting code here is proper etiquette, but I started testing out the REST API calls using PHP.  I am not too experience with PHP so there may be some coding issues, but I've tested it and it works fine with an API tool that I developed for development purposes.  The Classes basically. allow for testing of many of the API calls, but there are also views and some other pieces of code to make it work.  It is an MVC framework, but bare bones one.  Not too hard to plug it into something like Laravel.

There is a dev landing page for the UI, that send the request / posts to the Controller, and the Model helps process some of those request, along with just a couple of other views.

Apparently images can be included, so I will try to include screen captures of the views:

Dev page to test API calls:  The interface allows for testing all of the calls in the select list, no very readable.  The UUID's there are for testing because there a number of input fields that can be used to post.  Results are display in a modal div, like below for getting study data.

orthanc1.pngorthanc2.png

results.png



The PHP Controller:


<?php

class OrthancDevController extends Controller
{
   
/**
     * Construct this object by extending the basic Controller class

     */


   
// For API calls to AMBRA, filter out my access level inidividual calls later.
   
   
private $Orthanc;
   
private $postuuid;
   
private $withtags;
   
private $tagcodes;
   
private $display_results;
   
   
   
public function __construct()
   
   
{
        parent
::__construct();
       
       
Auth::checkAuthentication([7,8]);
       
       
if ($_SERVER['REQUEST_METHOD'] == "POST") {
        $this
->Orthanc = new OrthancModel;
        $this
->postuuid = $_POST["uuid"];
        $this
->withtags = $_POST["withtags"];
        $this
->tagcodes = $_POST["tagcodes"]; // can also be an array of code content/0008-1250/0/0040-a170/0/0008-0104
       
}
       
else {
            $this
->Orthanc = new OrthancModel;
       
}
       
       
// check the $_POST

   
}
   
   
public function readers() {
   
//             [uuid] => 0dfe0cd8-09750032-c51e0164-4514808d-b3758b72
//             [patient_name] => SCOTTI^STEPHEN^D
//             [patientid] => 1001915751
//             [patient_sex] => M
//             [birth_date] => 19571116
//             [accession_number] => A15382757
//             [referring_physican] => STROTHMAN^DAVID^HOWARD
//             [study_description] => MR SPINE LUMBAR WO
//             [institution] => ABBOTT NORTHWESTERN HOSPITAL
//             [study_date] => 20181130
//             [study_time] => 123600.181
//             [cpt_no_mods] => 72148.0
//             [instanceUID] => 1.2.826.0.1.3680043.2.133.1.3.1.36.26.99500
        $_SESSION
['view'] = "orthanc_reader";
        $allstudies
= $this->getStudies(true);
        $this
->View->render('readers/orthanc_index', array ("studies" => $allstudies, "omitold" => true));
   
}
   
   
public function renderiFrame ($src) {
   
   
   
   
}

   
public function apitool()
   
{
       
        $this
->View->render('apitool/orthancRESTtool');

   
}
   
// These all send null for the entire list or a uuid for just one.
   
   
function getPatients () {  // gets all or just a specific UUID.
//             var_dump($this->Orthanc->getPatients($this->postuuid));
            print_r
($this->Orthanc->getPatients($this->postuuid));
           
//var_dump($this->Orthanc->getPatients($this->postuuid));
           
   
}
   
   
function getStudies($return = false) {  // // gets all or just a specific UUID.

           
if (!empty($this->postuuid)) {
           
if($return) return $this->Orthanc->getStudyDetails($this->postuuid);
           
else echo print_r($this->Orthanc->getStudyDetails($this->postuuid));
           
}
           
else {
           
if($return) return $this->Orthanc->getStudies();
           
else echo print_r($this->Orthanc->getStudies());
           
}


//         $data['studies'] = $studydetails;
//         (new View)->renderWithoutHeaderAndFooter('studies/worklisttable', $data );
           
   
}
   
   
function getSeries() {  // gets a specific UUID

            print_r
( $this->Orthanc->getSeries($this->postuuid));
           
   
}
   
// this also has a $withtags option that can be simplified-tags or tag, for later
   
   
function getInstances() {  // gets a specific UUID
   
            print_r
($this->Orthanc->getInstances($this->postuuid, "simplified-tags"));
           
   
}
   
   
function getDICOMTagValueforUUID() {  // return empty mrn if no matches,  otherwise returns 1 result.  increment the suffix and return the result
   
            echo print_r
((array)$this->Orthanc->getDICOMTagValueforUUID($this->postuuid, $this->tagcodes), true);
           
   
}
   
   
function getInstanceDICOM() {  // return empty mrn if no matches,  otherwise returns 1 result.  increment the suffix and return the result
   
            echo print_r
((array)$this->Orthanc->getInstanceDICOM($this->postuuid), true);
           
   
}
   
   
function getInstancePNGPreview() {  // return empty mrn if no matches,  otherwise returns 1 result.  increment the suffix and return the result

            $image_data_base64
=  base64_encode ($this->Orthanc->getInstancePNGPreview($this->postuuid, "image/png" ));  // also image/jpeg
            echo
'<img src="data:image/png;base64,' . $image_data_base64 . 'alt="img"/ >';
           
   
}
   
   
function downloadZipStudyUUID() {
       
// disposition is inline for new window, attachment for download
           header
('Content-disposition:' .  'attachment; filename=download.zip');
           header
('Content-type: application/zip');
        echo $this
->Orthanc->downloadZipStudyUUID($this->postuuid);
   
}
   
   
function downloadDCMStudyUUID() {
   
        header
('Content-disposition:' .  'attachment; filename=download.zip');
        header
('Content-type: application/zip');
        echo $this
->Orthanc->downloadZipStudyUUID($this->postuuid);
   
}
   
   
function performQuery() {

           $result
= $this->Orthanc->performQuery("Study", '{"PatientID":"*","StudyDescription":"*","PatientName":"*"}');

           print_r
(json_decode($result));
       
   
}
   
   
function openViewerWithStudyUUID() {
   
        echo
'<iframe style="height:1300px;width:100%;" src="http://localhost:8042/osimis-viewer/app/index.html?study=' . $this->postuuid .  '"></iframe>';
   
}
   
   
function openOrthancViewer() {
   
            echo
'<iframe style="height:1300px;width:100%;" src="http://localhost:8042/"></iframe>';
   
}
}
?>


The Model:

<?php

Class OrthancModel  {

   
private $OrthancURL;
   
private $lastQueryID;
   
private $lastQueryPath;
   
private $mapFromOrthanc;
   
public $result;
   

   
public function __construct($data = null) {
   
        $this
->OrthancURL = "http://localhost:8042/";
        $this
->mapFromOrthanc = array(
       
           
"ID" => "uuid",
           
"PatientID" => "patientid",
           
"PatientBirthDate" => "birth_date",
           
"PatientName" => "hipaa_name",
           
"PatientSex" => "patient_sex",
           
"AccessionNumber" => "accession_number",
           
"ReferringPhysicianName" => "referring_physican",
           
"InstitutionName" => "institution",
           
"StudyDate" => "study_date",
           
"StudyTime" => "study_time",
           
"study_description" => "accession_number",
           
"StudyID" => "cpt_no_mods",  // ? CPT or other.
           
"StudyInstanceUID" => "study_uid"
   
);
   
}

   
public function executeCURL($CURLOPT_URL) {
   
       
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
        $ch
= curl_init();
        curl_setopt
($ch, CURLOPT_URL, $this->OrthancURL . $CURLOPT_URL);
        curl_setopt
($ch, CURLOPT_RETURNTRANSFER, 1);
        $this
->result = curl_exec($ch);
       
if (curl_errno($ch)) {
        echo
'Error:' . curl_error($ch);
       
}
        curl_close
($ch);

   
}
   
public function executeCURLWithHeaders($CURLOPT_URL, $headerfield = "") {
   
       
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
        $ch
= curl_init();
        curl_setopt
($ch, CURLOPT_URL, $this->OrthancURL . $CURLOPT_URL);
        curl_setopt
($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt
($ch, CURLOPT_CUSTOMREQUEST, 'GET');
        $headers
= array();
        $headers
[] = 'Accept: ' . $headerfield;
        curl_setopt
($ch, CURLOPT_HTTPHEADER, $headers);
        $this
->result = curl_exec($ch);
       
if (curl_errno($ch)) {
        echo
'Error:' . curl_error($ch);
       
}
        curl_close
($ch);
   
}
   
   
public function executeCURLQuery($object, $JSONQuery) {
   
       
// * is a wildcard in a search
       
// Dates are Ymd format, - is used for range, after or before.
       
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
       
       
// "ID": "5af318ac-78fb-47ff-b0b0-0df18b0588e0",  The ID can be used for subsequent queries
           
// "Path": "/queries/5af318ac-78fb-47ff-b0b0-0df18b0588e0"
       
        $ch
= curl_init();
        curl_setopt
($ch, CURLOPT_URL, $this->OrthancURL . '/tools/find');
        curl_setopt
($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt
($ch, CURLOPT_POST, 1);
        curl_setopt
($ch, CURLOPT_POSTFIELDS, '{"Level":"' . $object . '","Query":' . $JSONQuery . '}');
       
// {"PatientID":"","StudyDescription":"*Chest*","PatientName":""}

        $headers
= array();
        $headers
[] = 'Content-Type: application/x-www-form-urlencoded';
        curl_setopt
($ch, CURLOPT_HTTPHEADER, $headers);

        $this
->result = curl_exec($ch);
       
if (curl_errno($ch)) {
        echo
'Error:' . curl_error($ch);
       
}
        curl_close
($ch);
       
//         $this->lastQueryID = $result->ID;
//         $this->lastQueryPath = $result->Path;
   
}
   

   
public function getPatients($uuid = "") {
   
       
        $this
->executeCURL("patients/" . $uuid);
       
return json_decode($this->result);
       
   
}
   
   
public function getStudies() {  // same as constructor for subsequent calls.
   

        $this
->executeCURL("studies/");
        $uuidlist
=  json_decode($this->result);
       
//print_r($uuidlist);
        $studydetails
= array();
       
foreach ($uuidlist as $uuid) {
            $studydetails
[] = $this->getStudyDetails($uuid);
       
}
       
return $studydetails;
   
}
   
   
public function getStudyDetails ($uuid) {
   
        $this
->executeCURL("studies/" . $uuid);
        $raw
=  json_decode($this->result);
       
        $patientdata
= $raw->PatientMainDicomTags;
        $maindicom
= $raw->MainDicomTags;
        $details
= new \stdClass();
       
        $details
->uuid = $raw->ID;
        $details
->patient_name = $patientdata->PatientName;
        $details
->patientid = $patientdata->PatientID;
        $details
->patient_sex = $patientdata->PatientSex;
        $details
->birth_date = $patientdata->PatientBirthDate;
       
        $details
->accession_number = $maindicom->AccessionNumber;
        $details
->referring_physican = $maindicom->ReferringPhysicianName;
        $details
->study_description = $maindicom->StudyDescription;
        $details
->institution = $maindicom->InstitutionName;
        $details
->study_date = $maindicom->StudyDate;
        $details
->study_time = $maindicom->StudyTime;
        $details
->cpt_no_mods = $maindicom->StudyID;
        $details
->instanceUID = $maindicom->StudyInstanceUID;
       
       
return (array)$details;
       
       
   
}
   
   
public function getSeries($uuid = false) {  // same as constructor for subsequent calls.

        $this
->executeCURL("series/" . $uuid);
       
return json_decode($this->result);
       
   
}
   
   
public function getInstances($uuid = false, $withtags = false) {
   

        $withtags
= ($withtags?"/tags":"");  // detailed info
        $this
->executeCURL("instances/" . $uuid . $withtags);
       
return json_decode($this->result);
       
   
}
   
   
public function getDICOMTagValueforUUID ($uuid, $tagcodes) {
   
       
// can be a single value or an $array in which case it goes down the hierarchy.
       
if (is_array($tagcodes)) $tagcodes = implode("/", $tagcodes);

        $this
->executeCURL("instances/" . $uuid . '/content/' . $tagcodes);
       
return $this->result;
   
   
}
   
   
public function getDICOMTagListforUUID($uuid) {
   
        $this
->executeCURL("instances/" . $uuid . '/content');
       
return $this->result;
   
}
   
   
public function getInstanceDICOM($uuid) {
   
        $this
->executeCURL("instances/" . $uuid . '/file');
       
return $this->result;
   
   
}
   
   
public function getInstancePNGPreview ($uuid, $pngjpg) {
   
        $this
->executeCURLWithHeaders("instances/" . $uuid . '/preview/', $pngjpg);
       
return $this->result;
       
   
}
   
   
public function downloadZipStudyUUID ($uuid) {

        $this
->executeCURL("studies/" . $uuid . '/archive');
       
return $this->result;

       
   
}
   
   
public function downloadDCMStudyUUID ($uuid) {
   
        $this
->executeCURL("studies/" . $uuid . '/media');
       
return $this->result;
       
   
}

   
public function performQuery ($object, $JSONQuery) {
   
       
//{"Level":"Study","Query": {"PatientID":"","StudyDescription":"*Chest*","PatientName":""}}
        $this
->executeCURLQuery($object, $JSONQuery);
       
return $this->result;
       
   
}
/*

All instances of a study can be retrieved as a zip file as follows:

$ curl http://localhost:8042/studies/6b9e19d9-62094390-5f9ddb01-4a191ae7-9766b715/archive > Study.zip

It is also possible to download a zipped DICOMDIR through:

$ curl http://localhost:8042/studies/6b9e19d9-62094390-5f9ddb01-4a191ae7-9766b715/media > Study.zip


*/

   
}
?>



There is a view for listing the studies with a number of links for viewing .zip file download, reports, etc that integrates many of the API calls into a single view.


readerview.png









Sébastien Jodogne

unread,
Apr 27, 2020, 3:13:10 AM4/27/20
to Orthanc Users
Orthanc can evidently be installed on a dedicated hosted server.

Check out the Orthanc Book for information about how to obtain Orthanc binaries:

You can compile Orthanc by yourself, use Docker (https://book.orthanc-server.com/users/docker.html), use precompiled Linux Standard Base binaries that should work on CentOS (https://lsb.orthanc-server.com/), or use the official Debian packages (https://packages.debian.org/search?searchon=sourcenames&keywords=orthanc).

I will not analyze your PHP code: The subject of this discussion group is Orthanc.

Michel Rozpendowski

unread,
Apr 27, 2020, 8:45:16 AM4/27/20
to Orthanc Users
Hi,

On your dedicated host server, you can as well run Orthanc with Docker if you don't wish to compile Orthanc.

Michel
Reply all
Reply to author
Forward
0 new messages