Skip to content

Commit

Permalink
cornerstone: display NIfTI and dicom image previews
Browse files Browse the repository at this point in the history
  • Loading branch information
kousu committed Jan 15, 2023
1 parent d441e15 commit 8ed5e2e
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"node": ">= 14.0.0"
},
"dependencies": {
"cornerstone-core": "^2.6.1",
"cornerstone-tools": "^6.0.8",
"@cornerstonejs/nifti-image-loader": "^1.0.7",
"cornerstone-wado-image-loader": "^4.3.0",
"dicom-parser": "^1.8.19",
"@claviska/jquery-minicolors": "2.3.6",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "17.7.0",
Expand Down
4 changes: 4 additions & 0 deletions routers/web/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,10 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["IsAudioFile"] = true
case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()):
ctx.Data["IsImageFile"] = true
case strings.HasSuffix(strings.ToLower(blob.Name()), ".nii") || strings.HasSuffix(strings.ToLower(blob.Name()), ".nii.gz"):
ctx.Data["IsNIfTI"] = true
case strings.HasSuffix(strings.ToLower(blob.Name()), ".dcm") || strings.HasSuffix(strings.ToLower(blob.Name()), ".dicom"):
ctx.Data["IsDICOM"] = true
default:
if fileSize >= setting.UI.MaxDisplayFileSize {
ctx.Data["IsFileTooLarge"] = true
Expand Down
4 changes: 4 additions & 0 deletions templates/repo/view_file.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
</audio>
{{else if .IsPDFFile}}
<iframe width="100%" height="600px" src="{{AssetUrlPrefix}}/vendor/plugins/pdfjs/web/viewer.html?file={{$.RawFileLink}}"></iframe>
{{else if .IsNIfTI}}
<div class="cornerstone-viewport" style="width: 100%; height: 600px" data-src="nifti:{{$.RawFileLink}}"></div>
{{else if .IsDICOM}}
<div class="cornerstone-viewport" style="width: 100%; height: 600px" data-src="wadouri:{{$.RawFileLink}}"></div>
{{else}}
<a href="{{$.RawFileLink}}" rel="nofollow" class="btn btn-gray btn-radius">{{.locale.Tr "repo.file_view_raw"}}</a>
{{end}}
Expand Down
53 changes: 53 additions & 0 deletions web_src/js/features/cornerstone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* CornerstoneJS is a renderer and UI toolkit meant for
* medical image formats: .nii, .dcm, and some others.
*
* To use: create an element like
*
* <div class='.cornerstone-viewport' data-src='nifti:/path/to/file.nii.gz' style='width: 100%; height 500px'></div>
*
* and file.nii.gz will load when the page loads.
*
* Swap out 'nifti' for 'wadouri:' if using a DICOM file.
*
*
* **NOTE**: this is written using the _legacy_ cornerstone library,
* cornerstone-core, and is *not* compatible with the rewrite
* @cornerstonejs/core because @cornerstonejs/nifti-image-loader
* isn't: https://github.com/cornerstonejs/cornerstone-nifti-image-loader/issues/48#issuecomment-1271794397
* and rendering NIfTIs is the main reason this is here at all.
*/

export async function initCornerstone() {
if (!document.querySelector('.cornerstone-viewport')) return;

const cornerstone = await import( /* webpackChunkName: "cornerstone-core" */ 'cornerstone-core');
const dicomParser = await import( /* webpackChunkName: "dicom-parser" */ 'dicom-parser');
const cornerstoneWADOImageLoader = await import( /* webpackChunkName: "cornerstone-wado-image-loader" */ 'cornerstone-wado-image-loader');
const cornerstoneNIFTIImageLoader = await import( /* webpackChunkName: "@cornerstonejs/nifti-image-loader" */ '@cornerstonejs/nifti-image-loader');

// why are these necessary? too much abstraction, that's why.
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
cornerstoneNIFTIImageLoader.external.cornerstone = cornerstone

document.querySelectorAll('.cornerstone-viewport').forEach(function (canvas) {
if (canvas.dataset.src === undefined) {
return
}
cornerstone.enable(canvas)

console.info('Loading', canvas.dataset.src, 'into CornerstoneJS')
cornerstone
.loadAndCacheImage(canvas.dataset.src)
.then((image) => {
console.debug(image)

const viewport = cornerstone.getDefaultViewportForImage(canvas, image)
cornerstone.displayImage(canvas, image, viewport)
})
.catch((err) => {
console.error(err)
})
})

}
2 changes: 2 additions & 0 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import {initRepoWikiForm} from './features/repo-wiki.js';
import {initRepoCommentForm, initRepository} from './features/repo-legacy.js';
import {initFormattingReplacements} from './features/formatting.js';
import {initMcaptcha} from './features/mcaptcha.js';
import {initCornerstone} from './features/cornerstone.js';

// Run time-critical code as soon as possible. This is safe to do because this
// script appears at the end of <body> and rendered HTML is accessible at that point.
Expand Down Expand Up @@ -188,6 +189,7 @@ $(document).ready(() => {

initCommitStatuses();
initMcaptcha();
initCornerstone();

initUserAuthLinkAccountView();
initUserAuthOauth2();
Expand Down

0 comments on commit 8ed5e2e

Please sign in to comment.