DataInterface = $DataInterface; } /** * Post lesion area. */ public function acquireLesionPost($User, $data) { // $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT markers FROM visit WHERE ID = :fk_visit" ); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $markers = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['markers']); $markers[] = array( 'type' => $data['type'], 'location' => $data['location'], 'x' => $data['x'], 'y' => $data['y'] ); // $statement = $this->DataInterface->DatabaseConnection->prepare( "UPDATE visit SET markers = :markers WHERE ID = :fk_visit" ); $statement->bindParam(':markers', json_encode($markers)); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } return [ 'result' => 'OK', 'markers' => $markers ]; } /** * Delete lesion area. */ public function acquireLesionDeletePost($User, $data) { // $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT markers FROM visit WHERE ID = :fk_visit" ); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $markers = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['markers']); for($i=0; $ix) == intval($data['x']) && intval($markers[$i]->y) == intval($data['y']) && $markers[$i]->location == $data['location'] && $markers[$i]->type == $data['type']) { array_splice($markers, $i, 1); break; } } // $statement = $this->DataInterface->DatabaseConnection->prepare( "UPDATE visit SET markers = :markers WHERE ID = :fk_visit" ); $statement->bindParam(':markers', json_encode($markers)); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } return [ 'result' => 'OK', 'markers' => $markers ]; } /** * Post media. */ public function acquireMediaPost($User, $data) { // DICOM with unknown original side if(array_key_exists('source', $data) && $data['source']=='unknown') { $statement = $this->DataInterface->DatabaseConnection->prepare( "UPDATE media SET side = :side, location = :location, incidence = :incidence WHERE filename = :filename AND :fk_visit" ); $statement->bindParam(':side', $data['side']); $statement->bindParam(':location', $data['location']); $statement->bindParam(':incidence', $data['incidence']); $statement->bindParam(':filename', $data['filename']); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } return [ 'result' => 'OK', 'side' => $data['side'], 'location' => $data['location'], 'incidence' => $data['incidence'], 'filename' => $data['filename'] ]; } $filepath = $_SERVER['DOCUMENT_ROOT']."/storage/media/".$data['visitID'].'/'.$data['filename']; $metrics = ['width' => 0, 'height' => 0, 'pxwidth' => 0, 'pxheight' => 0]; // image JPEG if(substr_compare($data['filename'], '.jpeg', -strlen('.jpeg')) === 0) { list($metrics['width'], $metrics['height'], $type, $attr) = getimagesize($filepath); } // DICOM file else if(substr_compare($data['filename'], '.dicom', -strlen('.dicom')) === 0) { $jpegFilepath = str_replace('.dicom', '.jpeg', $filepath); if(file_exists($jpegFilepath)) { list($metrics['width'], $metrics['height'], $type, $attr) = getimagesize($jpegFilepath); } else { $mp4Filepath = str_replace('.dicom', '.mp4', $filepath); // get metrics $cmdLine = 'ffprobe -v error -show_entries stream=width,height,r_frame_rate -of csv=p=0 '.$mp4Filepath; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0 || count($output)!=1) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $tab = explode(',',trim($output[0])); if(count($tab)!=3) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $metrics['width'] = intval($tab[0]); $metrics['height'] = intval($tab[1]); $fps = $tab[2]; $tab = explode('/', $fps); if(count($tab) == 2) { $fps = intval($tab[0]); } else { $fps = intval($fps); } $metrics['fps'] = $fps; } // get metrics $cmdLine = 'dcmdump -s +P 0018,602c +P 0018,602e +P 0028,0008 "'.$filepath.'"'; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0 || (count($output)!=2 && count($output)!=3)) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $tab = explode(' ',trim($output[0])); $metrics['pxwidth'] = floatval($tab[2])*10.0; $tab = explode(' ',trim($output[1])); $metrics['pxheight'] = floatval($tab[2])*10.0; if(count($output)==3) { $tab = explode(' ',trim($output[2])); $metrics['frameCount'] = intval(str_replace(['[', ']'], '', $tab[2])); } } // video MP4 else if(substr_compare($data['filename'], '.mp4', -strlen('.mp4')) === 0) { // get metrics $cmdLine = 'ffprobe -v error -show_entries stream=width,height,r_frame_rate,nb_frames -of csv=p=0 '.$filepath; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0 || count($output)!=1) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $tab = explode(',',trim($output[0])); if(count($tab)!=4) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $metrics['width'] = intval($tab[0]); $metrics['height'] = intval($tab[1]); if(intval($tab[3])==0) { $metrics['frameCount'] = floor(intval($data['recordingTime'])/1000*11); $fps = 11; } else { $metrics['frameCount'] = intval($tab[3]); $fps = $tab[2]; $tab = explode('/', $fps); if(count($tab) == 2) { $fps = intval($tab[0]); } else { $fps = intval($fps); } } $metrics['fps'] = $fps; } else { return ['result' => 'ERROR', 'reason' => 'invalid_media']; } // if(array_key_exists('mediaID', $data)) { // select old media $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT * FROM media WHERE ID = ".$data['mediaID'] ); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $oldMedia = $statement->fetchAll(\PDO::FETCH_ASSOC)[0]; // delete old media $statement = $this->DataInterface->DatabaseConnection->prepare( "DELETE FROM media WHERE ID = ".$data['mediaID'] ); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } // insert new media $statement = $this->DataInterface->DatabaseConnection->prepare( "INSERT INTO media(side, location, incidence, filename, metrics, fk_visit) VALUES(:side, :location, :incidence, :filename, :metrics, :fk_visit)" ); $statement->bindParam(':side', $oldMedia['side']); $statement->bindParam(':location', $oldMedia['location']); $statement->bindParam(':incidence', $oldMedia['incidence']); $statement->bindParam(':filename', $data['filename']); $statement->bindParam(':metrics', json_encode($metrics)); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $mediaID = $this->DataInterface->DatabaseConnection->lastInsertId(); } else { $statement = $this->DataInterface->DatabaseConnection->prepare( "INSERT INTO media(side, location, incidence, filename, metrics, fk_visit) VALUES(:side, :location, :incidence, :filename, :metrics, :fk_visit)" ); $statement->bindParam(':side', $data['side']); $statement->bindParam(':location', $data['location']); $statement->bindParam(':incidence', $data['incidence']); $statement->bindParam(':filename', $data['filename']); $statement->bindParam(':metrics', json_encode($metrics)); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $mediaID = $this->DataInterface->DatabaseConnection->lastInsertId(); } return [ 'result' => 'OK', 'metrics' => $metrics, 'filename' => $data['filename'], 'mediaID' => $mediaID ]; } /** * Delete media. */ public function acquireDeletePost($User, $data) { $filename = $data['filename']; $statement = $this->DataInterface->DatabaseConnection->prepare( "INSERT INTO iimt_mathcloud_shadow.media SELECT * FROM media WHERE filename = '$filename'; DELETE FROM media WHERE filename = '$filename';" ); // Error check if(!$statement->execute()) { return array('result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()); } return [ 'result' => 'OK' ]; } /** * Post acquire area. */ public function acquireAreaPost($User, $data) { // $statement = $this->DataInterface->DatabaseConnection->prepare( "UPDATE visit SET area = :area WHERE ID = :fk_visit" ); $statement->bindParam(':area', $data['area']); $statement->bindParam(':fk_visit', $data['visitID']); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } return [ 'result' => 'OK' ]; } /** * */ public function acquireDownloadGet($User, $visitID, $filename) { // media $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT * FROM media WHERE filename = :filename" ); $statement->bindParam(':filename', $filename); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $path = '../../storage/media/'.$visitID.'/'.$statement->fetchAll(\PDO::FETCH_ASSOC)[0]['filename']; $data = file_get_contents($path); $base64 = /*'data:image/' . $type . ';base64,' .*/ base64_encode($data); $type = pathinfo($path, PATHINFO_EXTENSION); if($type=='dicom') { $type = 'application/'.$type; } else if($type=='mp4') { $type = 'video/'.$type; } else { $type = 'image/'.$type; } return [ 'result' => 'OK', 'media' => $path, 'type' => $type, 'base64' => $base64 ]; } /** * Get media */ public function acquireMediaGet($User, $patientID, $visitID) { // patient $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT patient.* FROM patient WHERE ID = :fk_patient" ); $statement->bindParam(':fk_patient', $patientID); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0]; // visit $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT area, markers FROM visit WHERE ID = :fk_visit" ); $statement->bindParam(':fk_visit', $visitID); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $data = $statement->fetchAll(\PDO::FETCH_ASSOC)[0]; // media $statement = $this->DataInterface->DatabaseConnection->prepare( "SELECT * FROM media WHERE fk_visit = :fk_visit" ); $statement->bindParam(':fk_visit', $visitID); // Error check if(!$statement->execute()) { return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()]; } $media = $statement->fetchAll(\PDO::FETCH_ASSOC); foreach($media as &$m) { $m['metrics'] = json_decode($m['metrics']); } return [ 'result' => 'OK', 'patient' => $patient, 'area' => $data['area'], 'markers' => json_decode($data['markers']), 'media' => $media ]; } /** * Post upload data. */ public function acquireUploadPost($User, $data) { $baseDir = $_SERVER['DOCUMENT_ROOT']."/storage/media/".$data['visitID']; \Tools\FS::mkpath($baseDir); $ext = explode('/',$data['type'])[1]; $filename = \Tools\UUID::v4().'.'.$ext; // Unknown if($data['type']=="") { $ext = 'dicom'; $data['type'] = 'application/dicom'; $data['data'] = str_replace('application/octet-stream', 'application/dicom', $data['data']); $filename .= $ext; } // DICOM image if(strtolower($ext)=='dicom') { $dcmData = $data['data']; $dcmData = str_replace('data:'.$data['type'].';base64,', '', $dcmData); $dcmData = base64_decode($dcmData); // Base64 decoded data $ok = file_put_contents($baseDir.'/'.$filename, $dcmData); // error if($ok === false) { return [ 'result' => 'ERROR', 'filename' => $filename, 'reason' => 'write', 'message' => error_get_last()['message'] ]; } // multiframe? $cmdLine = 'dcmdump +P 0028,0008 "'.$baseDir.'/'.$filename.'"'; $output=null; $retval=null; exec($cmdLine, $output, $retval); // OK if($retval === 0 && count($output)==1) { $tab = explode(' ',trim($output[0])); $frameCount = intval(str_replace(['[', ']'], '', $tab[2])); // error if($frameCount===0) { return [ 'result' => 'ERROR', 'output' => $output, ]; } // make JPEG $frames = []; for($i=1; $i<=$frameCount; $i++) { $src = $baseDir.'/'.$filename; $pad = str_pad($i, 3, "0", STR_PAD_LEFT); $dst = str_replace('.dicom', '-'.$pad.'.jpeg', $src); $cmdLine = 'dcmj2pnm "'.$src.'" "'.$dst.'" +oj +Jq 100 +F '.$i.' -v 2>&1'; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0 || count($output)<1) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $frames[] = $dst; } // make MP4 $src = $baseDir.'/'.$filename; $dst = str_replace('.dicom', '-%03d.jpeg', $src); $mp4 = str_replace('.dicom', '.mp4', $src); $cmdLine = 'ffmpeg -f image2 -pattern_type sequence -r 11 -i '.$dst.' -y -c:v libx264 -pix_fmt yuv420p '.$mp4; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } // cleanup for($i=0; $i 'OK', 'filename' => $filename, 'content' => $base64, 'type' => 'dicom' ]; } // singleframe else { $src = $baseDir.'/'.$filename; $dst = str_replace('.dicom', '.jpeg', $src); $cmdLine = 'dcmj2pnm "'.$src.'" "'.$dst.'" +oj +Jq 100 +F 1 -v 2>&1'; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0 || count($output)<1) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $data = file_get_contents($dst); $base64 = 'data:image/jpeg;base64,'.base64_encode($data); return [ 'result' => 'OK', 'filename' => $filename, 'content' => $base64, 'type' => 'dicom' ]; } } // AVI video else if($ext == 'x-msvideo') { $videoData = $data['data']; $videoData = str_replace('data:'.$data['type'].';base64,', '', $videoData); $videoData = base64_decode($videoData); // Base64 decoded data $ok = file_put_contents($baseDir.'/'.$filename, $videoData); // error if($ok === false) { return [ 'result' => 'ERROR', 'filename' => $filename, 'reason' => 'write', 'message' => error_get_last()['message'] ]; } $src = $baseDir.'/'.$filename; $dst = str_replace('.x-msvideo', '.mp4', $src); $cmdLine = 'ffmpeg -i '.$src.' -y -r 11 '.$dst; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $data = file_get_contents($dst); $base64 = 'data:video/mp4;base64,'.base64_encode($data); return [ 'result' => 'OK', 'filename' => str_replace('.x-msvideo', '.mp4', $filename), 'content' => $base64, 'type' => 'mp4' ]; } // MP4 video else if($ext == 'mp4') { $videoData = $data['data']; //file_put_contents($baseDir.'/'.$filename.'-data-prev.txt', $videoData); $videoData = str_replace('data:'.$data['type'].';base64,', '', $videoData); //file_put_contents($baseDir.'/'.$filename.'-data-after.txt', $videoData); $videoData = base64_decode($videoData); // Base64 decoded data $src = $baseDir.'/'.$filename; $ok = file_put_contents($src, $videoData); // error if($ok === false) { return [ 'result' => 'ERROR', 'filename' => $filename, 'reason' => 'write', 'message' => error_get_last()['message'] ]; } $base64 = $data['data']; //same as: //$data = file_get_contents($src); //$base64 = 'data:video/mp4;base64,'.base64_encode($data); return [ 'result' => 'OK', 'filename' => $filename, 'content' => $base64, 'type' => 'mp4' ]; } // JPEG image else if($ext == 'jpeg') { $imgData = $data['data']; $imgData = str_replace('data:'.$data['type'].';base64,', '', $imgData); $imgData = base64_decode($imgData); // Base64 decoded data $ok = file_put_contents($baseDir.'/'.$filename, $imgData); // error if($ok === false) { return [ 'result' => 'ERROR', 'filename' => $filename, 'reason' => 'write', 'message' => error_get_last()['message'] ]; } return [ 'result' => 'OK', 'filename' => $filename, 'type' => 'jpeg' ]; } // Captured video else if($ext == 'webm') { $videoData = $data['data']; $videoData = str_replace('data:'.$data['type'].';base64,', '', $videoData); $videoData = base64_decode($videoData); // Base64 decoded data $ok = file_put_contents($baseDir.'/'.$filename, $videoData); // error if($ok === false) { return [ 'result' => 'ERROR', 'filename' => $filename, 'reason' => 'write', 'message' => error_get_last()['message'] ]; } $src = $baseDir.'/'.$filename; $dst = str_replace('.webm', '.mp4', $src); $cmdLine = 'ffmpeg -i '.$src.' -y -r 11 '.$dst; $output=null; $retval=null; exec($cmdLine, $output, $retval); // error if($retval !== 0) { return [ 'result' => 'ERROR', 'cmdLine' => $cmdLine, 'output' => $output, 'retval' => $retval ]; } $data = file_get_contents($dst); $base64 = 'data:video/mp4;base64,'.base64_encode($data); return [ 'result' => 'OK', 'filename' => str_replace('.webm', '.mp4', $filename), 'content' => $base64, 'type' => 'mp4' ]; } return [ 'result' => 'ERROR', 'reason' => 'invalid_media', 'filename' => $filename ]; } } }