Skip to content

Commit

Permalink
Merge pull request #1064 from PintoGideon/master
Browse files Browse the repository at this point in the history
Implement support for jpg/png in cornerstone
  • Loading branch information
PintoGideon authored Feb 15, 2024
2 parents 4672591 + 2057d30 commit 22b7bc1
Show file tree
Hide file tree
Showing 14 changed files with 3,211 additions and 320 deletions.
2,671 changes: 2,577 additions & 94 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
"print-version": "./print_version.sh"
},
"dependencies": {
"@cornerstonejs/core": "^1.57.0",
"@cornerstonejs/core": "^1.58.5",
"@cornerstonejs/dicom-image-loader": "^1.57.0",
"@cornerstonejs/nifti-image-loader": "^1.0.9",
"@cornerstonejs/tools": "^1.57.0",
"@cornerstonejs/streaming-image-volume-loader": "^1.58.5",
"@cornerstonejs/tools": "^1.58.5",
"@fnndsc/chrisapi": "^1.15.0",
"@heroicons/react": "^2.1.1",
"@niivue/niivue": "^0.38.8",
Expand All @@ -41,10 +41,8 @@
"antd": "^5.13.2",
"axios": "^1.6.5",
"chris-utility": "^1.1.6",
"cornerstone-core": "^2.6.1",
"cornerstone-file-image-loader": "^0.3.0",
"cornerstone-math": "^0.1.10",
"cornerstone-tools": "^6.0.10",
"cornerstone-wado-image-loader": "^4.13.2",
"d3-hierarchy": "^1.1.9",
"d3-selection": "^1.4.2",
Expand All @@ -61,6 +59,7 @@
"micromark": "^4.0.0",
"micromark-extension-gfm": "^3.0.0",
"niivue-react": "github:niivue/niivue-react",
"npm": "^10.4.0",
"pako": "^1.0.11",
"preval.macro": "^5.0.0",
"rc-tree": "^5.8.0",
Expand Down
2 changes: 0 additions & 2 deletions src/components/Feeds/FeedListView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useContext, useMemo } from "react";

import { useLocation, useNavigate } from "react-router";
import { useDispatch } from "react-redux";
import { format } from "date-fns";
Expand Down Expand Up @@ -452,7 +451,6 @@ function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
return selectedFeed.data.id !== feed.data.id;
});
const selectAllToggle = filteredBulkSelect.length === allFeeds.length;

dispatch(removeBulkSelect(filteredBulkSelect, selectAllToggle));
}
}}
Expand Down
3 changes: 1 addition & 2 deletions src/components/Preview/FileDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
} from "@patternfly/react-core";
import { useQuery } from "@tanstack/react-query";
import { ErrorBoundary } from "react-error-boundary";

import ZoomIcon from "@patternfly/react-icons/dist/esm/icons/search-plus-icon";
import PanIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
import RotateIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
Expand All @@ -28,7 +27,7 @@ import { getFileExtension } from "../../api/model";
import { IFileBlob, fileViewerMap } from "../../api/model";
import { SpinContainer } from "../Common";
import { TagInfoModal } from "./HelperComponent";
import { dumpDataSet } from "./utils";
import { dumpDataSet } from "./displays/dicomUtils/dicomDict";

const ViewerDisplay = React.lazy(() => import("./displays/ViewerDisplay"));

Expand Down
2 changes: 0 additions & 2 deletions src/components/Preview/displays/CatchallDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const CatchallDisplay: React.FunctionComponent<AllProps> = (
const ext = fileItem.fileType ? fileItem.fileType : "";
const alertText = (
<React.Fragment>
<label></label>
<br></br>
<label>
<b>File Type:</b> {ext}
</label>
Expand Down
13 changes: 9 additions & 4 deletions src/components/Preview/displays/DcmDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import * as React from "react";
import { IFileBlob, getFileExtension } from "../../../api/model";
import {
FileViewerModel,
IFileBlob,
getFileExtension,
} from "../../../api/model";
import {
displayDicomImage,
loadDicomImage,
loadJPGImage,
basicInit,
handleEvents,
type IStackViewport,
} from "../utils";
} from "./dicomUtils/utils";
import useSize from "../../FeedTree/useSize";
import { type ActionState } from "../FileDetailView";
import { _loadImageIntoBuffer } from "./dicomUtils/webImageLoader";

export type DcmImageProps = {
fileItem: IFileBlob;
Expand Down Expand Up @@ -37,7 +41,8 @@ const DcmDisplay: React.FC<DcmImageProps> = (props: DcmImageProps) => {
if (extension === "dcm") {
imageID = await loadDicomImage(blob);
} else {
imageID = loadJPGImage(blob);
const fileName = FileViewerModel.getFileName(file);
imageID = `web:${file.url}${fileName}`;
}
const activeViewport = await displayDicomImage(element, imageID);
setActiveViewport(activeViewport);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Preview/displays/PdfDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const PdfDisplay: React.FC<AllProps> = ({ fileItem }: AllProps) => {
return (
<div className="iframe-container">
<iframe
key={fileItem.file && fileItem.file.data.fname}
key={fileItem.file?.data.fname}
src={url}
width="100%"
height="100%"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,214 +1,6 @@
import { RenderingEngine, init, Types, Enums } from "@cornerstonejs/core";
import {
init as csToolsInit,
Types as CornerstoneToolTypes,
} from "@cornerstonejs/tools";

import dicomParser from "dicom-parser";
import * as cornerstone from "@cornerstonejs/core";
import * as cornerstoneTools from "@cornerstonejs/tools";
import * as cornerstoneFileImageLoader from "cornerstone-file-image-loader";
import cornerstonejsDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
import ptScalingMetaDataProvider from "./ptScalingMetaDataProvider";
import { TAG_DICT, uids } from "./dataDictionary";
import Rusha from "rusha";

//@ts-ignore
window.cornerstone = cornerstone;
//@ts-ignore
window.cornerstoneTools = cornerstoneTools;
const { preferSizeOverAccuracy, useNorm16Texture } =
cornerstone.getConfiguration().rendering;

const { ViewportType } = Enums;

const { calibratedPixelSpacingMetadataProvider } = cornerstone.utilities;

const {
PanTool,
WindowLevelTool,
StackScrollMouseWheelTool,
ZoomTool,
PlanarRotateTool,
MagnifyTool,
LengthTool,
ToolGroupManager,
Enums: csToolsEnums,
} = cornerstoneTools;
const { MouseBindings } = csToolsEnums;

function initProviders() {
cornerstone.metaData.addProvider(
ptScalingMetaDataProvider.get.bind(ptScalingMetaDataProvider),
10000,
);
cornerstone.metaData.addProvider(
calibratedPixelSpacingMetadataProvider.get.bind(
calibratedPixelSpacingMetadataProvider,
),
11000,
);
}

let toolGroup: CornerstoneToolTypes.IToolGroup | undefined;
let alreadyAdded = false;
const renderingEngineId = "myRenderingEngine";
const viewportId = "CT_STACK";

function setUpTooling() {
if (!alreadyAdded) {
// Add tools to Cornerstone3D
const toolGroupId = "STACK_TOOL_GROUP_ID";
cornerstoneTools.addTool(LengthTool);
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(WindowLevelTool);
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(ZoomTool);
cornerstoneTools.addTool(MagnifyTool);
cornerstoneTools.addTool(PlanarRotateTool);

toolGroup = ToolGroupManager.createToolGroup(toolGroupId);

if (toolGroup) {
// Add tools to the tool group

toolGroup.addTool(WindowLevelTool.toolName);
toolGroup.addTool(PanTool.toolName);
toolGroup.addTool(ZoomTool.toolName);
toolGroup.addTool(StackScrollMouseWheelTool.toolName, { loop: false });
toolGroup.addTool(PlanarRotateTool.toolName);
toolGroup.addTool(LengthTool.toolName);
toolGroup.addTool(MagnifyTool.toolName);
}
alreadyAdded = true;
}
}

export const initializeCornerstoneForDicoms = () => {
cornerstonejsDICOMImageLoader.external.cornerstone = cornerstone;
cornerstonejsDICOMImageLoader.external.dicomParser = dicomParser;
cornerstoneFileImageLoader.external.cornerstone = cornerstone;
cornerstonejsDICOMImageLoader.configure({
useWebWorkers: true,
decodeConfig: {
convertFloatPixelDataToInt: false,
use16BitDataType: preferSizeOverAccuracy || useNorm16Texture,
},
});

let maxWebWorkers = 1;

if (navigator.hardwareConcurrency) {
maxWebWorkers = Math.min(navigator.hardwareConcurrency, 7);
}

const config = {
maxWebWorkers,
startWebWorkersOnDemand: false,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
strict: false,
},
},
};

cornerstonejsDICOMImageLoader.webWorkerManager.initialize(config);
};

export const basicInit = async () => {
initProviders();
initializeCornerstoneForDicoms();
await init();
csToolsInit();
setUpTooling();
};

const actionStateTools = [
"Zoom",
"Pan",
"WindowLevel",
"PlanarRotate",
"Length",
"Magnify",
"Reset",
];

export type IStackViewport = Types.IStackViewport;

export const handleEvents = (
actionState: { [key: string]: boolean },
activeViewport?: IStackViewport,
) => {
for (const toolName of actionStateTools) {
if (toolName === "Reset") {
activeViewport?.resetCamera(true, true);
continue;
}

const isActive = actionState[toolName];

if (isActive) {
// Set the current tool as active
toolGroup?.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }],
});
} else {
// Disable all other tools
toolGroup?.setToolDisabled(toolName);
}
}
};

export const loadDicomImage = (blob: Blob) => {
return cornerstonejsDICOMImageLoader.wadouri.fileManager.add(blob);
};

export const loadJPGImage = (blob: Blob) => {
return cornerstoneFileImageLoader.fileManager.add(blob);
};

export const displayDicomImage = async (
element: HTMLDivElement,
imageId: string,
) => {
try {
const renderingEngine = new RenderingEngine(renderingEngineId);

const viewportInput = {
viewportId,
element,
type: ViewportType.STACK,
defaultOptions: {
background: <Types.Point3>[0.2, 0, 0.2],
},
};

renderingEngine.enableElement(viewportInput);

toolGroup?.addViewport(viewportId, renderingEngineId);

const viewport = <Types.IStackViewport>(
renderingEngine.getViewport(viewportId)
);

// Define a stack containing a single image
const stack = [imageId];

// Set the stack on the viewport
await viewport.setStack(stack);

cornerstoneTools.utilities.stackPrefetch.enable(viewport.element);

// Render the image
viewport.render();

return viewport;
} catch (error) {
console.log("Error", error);
}
};

function imageFrameLink(frameIndex: any) {
let linkText = "<a class='imageFrameDownload' ";
linkText += "data-frameIndex='" + frameIndex + "'";
Expand Down
Loading

0 comments on commit 22b7bc1

Please sign in to comment.