When i open this study on stone viewer, select series with many instance frames and scroll each frame is being retrieved slow(5-20 seconds). The same for OHIF, cornerstone js.
Is there any plugin to cache frame content? I can cache frames only for mammo studies, because mammo frames are 5-10mb and frames of other modalities are small enough to get them instantly, i don’t even see loading indicator when i scroll.
// Maximum size of the storage cache in MB. The storage cache
// is stored in RAM and contains a copy of recently accessed
// files (written or read). A value of "0" indicates the cache
// is disabled. (new in Orthanc 1.10.0)
"MaximumStorageCacheSize" : 128,
current value is 128. We upload many studies with thousand of frames an hour. 8 gb of ram can contain only last 1 such mammo study. Is there a way to store frame content in a disk, not in ram? maybe in swap?
But have you tried increasing the value ? Caching can happen when viewing to avoid the whole file being transcoded every time you want to extract a single frame.
I set to 8000. In docker stats i can see that it increases to 6-8 gb when i open the study. But frames still are being retrieved in 5-8 seconds even for 1 instance that has 123 frames in OHIF.
May be OHIF uses dicom-web and dicom-web plugin doesn;t care about MaximumStorageCacheSize?
I actually wrote some plugin, it works good. But i have some questions in code comments:
dir_path = os.path.dirname(os.path.realpath(__file__))
def OnChange(changeType, level, resource):
if changeType == orthanc.ChangeType.NEW_INSTANCE:
newInstance = json.loads(orthanc.RestApiGet('/instances/%s' % resource))
query = {}
query['Expand'] = True
query['Level'] = "Instance"
query['Query'] = { 'SOPInstanceUID': newInstance['MainDicomTags']['SOPInstanceUID'] }
query['RequestedTags'] = ["StudyInstanceUID", "SeriesInstanceUID"]
instanceMetadata = json.loads(orthanc.RestApiPost('/tools/find', json.dumps(query)))
StudyInstanceUID = instanceMetadata[0]['RequestedTags']['StudyInstanceUID']
SeriesInstanceUID = instanceMetadata[0]['RequestedTags']['SeriesInstanceUID']
SOPInstanceUID = newInstance['MainDicomTags']['SOPInstanceUID']
NumberOfFrames = newInstance['MainDicomTags'].get('NumberOfFrames', None)
if(NumberOfFrames == None):
return
for i in range(int(NumberOfFrames)):
frame = i
# b = requests.get(f'http://localhost:8042/dicom-web/studies/{StudyInstanceUID}/series/{SeriesInstanceUID}/instances/{SOPInstanceUID}/frames/{frame}',
# auth=(os.getenv('ORTHANC_USERNAME'), os.getenv('ORTHANC_PASSWORD')))
# this works a lot faster than dicom-web above. Do you know why? any caveats? maybe because dicom-web plugin doesn't care about MaximumStorageCacheSize?
b = requests.get(f'http://localhost:8042/instances/{resource}/frames/{frame}/raw',
auth=(os.getenv('ORTHANC_USERNAME'), os.getenv('ORTHANC_PASSWORD')))
cacheFolder = os.path.join(dir_path, f'../python_tmp/cache/{StudyInstanceUID}/{SeriesInstanceUID}/{SOPInstanceUID}')
os.makedirs(cacheFolder, exist_ok=True)
file_path = os.path.join(cacheFolder, str(frame + 1))
with open(file_path, 'wb') as file:
file.write(b.content)
orthanc.RegisterOnChangeCallback(OnChange)
It creates study/series/instance/1-2-3 frames files.
Here is route to serve them
def OnRest(output, uri, **request):
res = 'ok'
pattern = r"/dicom/studies/([^/]+)/series/([^/]+)/instances/([^/]+)/frames/([^/]+)"
print(1, uri)
match = re.search(pattern, uri)
if match:
StudyInstanceUID = match.group(1)
SeriesInstanceUID = match.group(2)
SOPInstanceUID = match.group(3)
frame = match.group(4)
cacheFolder = os.path.join(dir_path, f'../python_tmp/cache/{StudyInstanceUID}/{SeriesInstanceUID}/{SOPInstanceUID}')
with open(cacheFolder + f'/{str(frame)}', 'rb') as f:
res = f.read()
output.AnswerBuffer(res, 'application/octet-stream')
# output.AnswerBuffer(res, 'multipart/related; type="application/octet-stream; transfer-syntax=1.2.840.10008.1.2.1"; boundary=e156a009-a9fc-4206-bd96-6180955b3089-b8509407-a64b-4d42-a78b-95269a60f')
# output.AnswerBuffer(res, 'application/gzip')
# which one from above has correct content type? Every of them works. is boundary necessary?
orthanc.RegisterRestCallback('/dicom/(.*)', OnRest)
When i scroll i don’t even see loading indicator in OHIF. If plugin has some caveats or improvements, please tell me where. I’m going to implement auto deleting of old studies.
Thanks
I set image: orthancteam/orthanc-pre-release:master-full-unstable and it really works fast. Since clinic’s pc with Orthanc is not powerful enough to use my implementation with caching i’m gonna use full-unstable version for now. Is it too risky? If this feature will be in stable version in a week i think we can wait. Thanks again