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

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.

CentOS installation

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 ''; } 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 ''; } function openOrthancViewer() { echo ''; } } ?>

`

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

Orthanc can evidently be installed on a dedicated hosted server.

Check out the Orthanc Book for information about how to obtain Orthanc binaries:
https://book.orthanc-server.com/users/cookbook.html#obtaining-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.

Hi,

On your dedicated host server, you can as well run Orthanc with Docker if you don’t wish to compile Orthanc.
See https://book.orthanc-server.com/users/docker.html and https://book.orthanc-server.com/users/docker-osimis.html

Cheers,

Michel