feat: Add measurement tracking user guide
This commit is contained in:
parent
39ff15aeaf
commit
3fc56f373c
@ -5,12 +5,15 @@ sidebar_position: 4
|
|||||||
# Measurement Tracking
|
# Measurement Tracking
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
OHIF-V3's `Basic Viewer` implements a `Measurement Tracking` workflow. Measurement
|
||||||
|
tracking allows you to:
|
||||||
|
|
||||||
|
- Draw annotations and have them shown in the measurement panel
|
||||||
|
- Create a report from the tracked measurement and export them as DICOM SR
|
||||||
|
- Use already exported DICOM SR to re-hydrate the measurements in the viewer
|
||||||
|
|
||||||
|
|
||||||
|
## Status Icon
|
||||||
|
|
||||||
|
|
||||||
### Tracked Icon
|
|
||||||
Each viewport has a left icon indicating whether the series within the viewport
|
Each viewport has a left icon indicating whether the series within the viewport
|
||||||
contains:
|
contains:
|
||||||
|
|
||||||
@ -19,39 +22,74 @@ contains:
|
|||||||
- Structured Report OR
|
- Structured Report OR
|
||||||
- Locked (uneditable) Structured Report
|
- Locked (uneditable) Structured Report
|
||||||
|
|
||||||
|
In the following, we will discuss each category.
|
||||||
|
|
||||||
|
### Tracked vs Untracked Measurements
|
||||||
|
|
||||||
|
`OHIF-v3` implements a workflow for measurement tracking that can be seen below.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In summary, when you create an annotation, a prompt will be shown whether to start tracking or not. If you start the tracking, the annotation style will change to a solid line, and annotation details get displayed on the measurement panel.
|
||||||
|
On the other hand, if you decline the tracking pormpt, the measurement will be considered "temporary," and annotation style remains as a dashed line and not shown on the right panel, and cannot be exported.
|
||||||
|
|
||||||
|
|
||||||
|
Below, you can see different icons that appear for a tracked vs. untracked series in
|
||||||
|
`OHIF-v3`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
when you export an SR you will see, and also draggin and dropping SR inside viewer
|
|
||||||
|
|
||||||
|
Overview video for starting the tracking for measurements:
|
||||||
|
<!-- trackedMeasurements -->
|
||||||
|
TEMP
|
||||||
|
|
||||||
|
Overview video for not starting tracking for measurements:
|
||||||
|
<!-- untrackedMeasurements -->
|
||||||
|
TEMP
|
||||||
|
|
||||||
|
|
||||||
|
### Reading and Writing DICOM SR
|
||||||
|
|
||||||
|
`OHIF-v3` provides full support for reading, writing and mapping the DICOM Structured
|
||||||
|
Report (SR) to interactable `Cornerstone Tools`. When you load an already exported
|
||||||
|
DICOM SR into the viewer, you will be prompted whether to track the measurements
|
||||||
|
for the series or not.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you say no and later decide to say yes, click on the SR button the prompt will come again
|
If you click Yes, DICOM SR measurements gets re-hydrated into the viewer and
|
||||||
|
the series become a tracked series. However, If you say no and later decide to say track the measurements, you can always click on the SR button that will prompt you
|
||||||
click to restore measurements
|
with the same message again.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
The full workflow for saving measurements to SR and loading SR into the viewer is shown below.
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Overview video for loading DICOM SR and making a tracked sereis:
|
||||||
|
<!-- SR Restore and Yes -->
|
||||||
|
TEMP
|
||||||
|
|
||||||
|
Overview video for loading DICOM SR and not making a tracked series:
|
||||||
|
<!-- SR Restore and No -->
|
||||||
|
<!-- SR Export and measurement restore -->
|
||||||
|
TEMP
|
||||||
|
|
||||||
|
### Loading DICOM SR into an Already Tracked Series
|
||||||
|
|
||||||
|
If you have an already tracked series and try to load a DICOM SR measurements,
|
||||||
|
you will be shown the following lock icon. This means that, you can review the
|
||||||
|
DICOM SR measurement, manipulate image and draw "temporary" measurements; however,
|
||||||
|
you cannot edit the DICOM SR measurement.
|
||||||
|
|
||||||
Locked SR
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Overview video for loading DICOM SR inside an already tracked sereis:
|
||||||
|
<!-- DICOMSRLockedTracked -->
|
||||||
|
TEMP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Workflow for tracking can be found below. You can click on the image to have a zoomed in
|
|
||||||
version pop up or you can also right click on the image
|
|
||||||
and click `Open image in new tab` to have a better experience reading the details.
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|||||||
@ -15,13 +15,13 @@
|
|||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.0.0-beta.0",
|
"@docusaurus/core": "2.0.0-beta.3",
|
||||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.0",
|
"@docusaurus/plugin-client-redirects": "2.0.0-beta.3",
|
||||||
"@docusaurus/plugin-ideal-image": "2.0.0-beta.0",
|
"@docusaurus/plugin-ideal-image": "2.0.0-beta.3",
|
||||||
"@docusaurus/plugin-pwa": "2.0.0-beta.0",
|
"@docusaurus/plugin-pwa": "2.0.0-beta.3",
|
||||||
"@docusaurus/preset-classic": "2.0.0-beta.0",
|
"@docusaurus/preset-classic": "2.0.0-beta.3",
|
||||||
"@docusaurus/remark-plugin-npm2yarn": "2.0.0-beta.0",
|
"@docusaurus/remark-plugin-npm2yarn": "2.0.0-beta.3",
|
||||||
"@docusaurus/theme-live-codeblock": "2.0.0-beta.0",
|
"@docusaurus/theme-live-codeblock": "2.0.0-beta.3",
|
||||||
"@mdx-js/react": "^1.6.21",
|
"@mdx-js/react": "^1.6.21",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
|
|||||||
@ -8,21 +8,17 @@ import React from 'react';
|
|||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import Translate from '@docusaurus/Translate';
|
import Translate from '@docusaurus/Translate';
|
||||||
import {
|
import { useActivePlugin, useDocVersionSuggestions } from '@theme/hooks/useDocs';
|
||||||
useActivePlugin,
|
|
||||||
useActiveVersion,
|
|
||||||
useDocVersionSuggestions,
|
|
||||||
} from '@theme/hooks/useDocs';
|
|
||||||
import { useDocsPreferredVersion } from '@docusaurus/theme-common';
|
import { useDocsPreferredVersion } from '@docusaurus/theme-common';
|
||||||
|
|
||||||
function UnreleasedVersionLabel({ siteTitle, versionLabel }) {
|
function UnreleasedVersionLabel({ siteTitle, versionMetadata }) {
|
||||||
return (
|
return (
|
||||||
<Translate
|
<Translate
|
||||||
id="theme.docs.versions.unreleasedVersionLabel"
|
id="theme.docs.versions.unreleasedVersionLabel"
|
||||||
description="The label used to tell the user that he's browsing an unreleased doc version"
|
description="The label used to tell the user that he's browsing an unreleased doc version"
|
||||||
values={{
|
values={{
|
||||||
siteTitle,
|
siteTitle,
|
||||||
versionLabel: <strong>{versionLabel}</strong>,
|
versionLabel: <b>{versionMetadata.label}</b>,
|
||||||
latestVersionLink: (
|
latestVersionLink: (
|
||||||
<strong>
|
<strong>
|
||||||
<Link to={"/"}>
|
<Link to={"/"}>
|
||||||
@ -42,14 +38,14 @@ function UnreleasedVersionLabel({ siteTitle, versionLabel }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UnmaintainedVersionLabel({ siteTitle, versionLabel }) {
|
function UnmaintainedVersionLabel({ siteTitle, versionMetadata }) {
|
||||||
return (
|
return (
|
||||||
<Translate
|
<Translate
|
||||||
id="theme.docs.versions.unmaintainedVersionLabel"
|
id="theme.docs.versions.unmaintainedVersionLabel"
|
||||||
description="The label used to tell the user that he's browsing an unmaintained doc version"
|
description="The label used to tell the user that he's browsing an unmaintained doc version"
|
||||||
values={{
|
values={{
|
||||||
siteTitle,
|
siteTitle,
|
||||||
versionLabel: <strong>{versionLabel}</strong>,
|
versionLabel: <b>{versionMetadata.label}</b>,
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
'This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.'
|
'This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.'
|
||||||
@ -58,15 +54,26 @@ function UnmaintainedVersionLabel({ siteTitle, versionLabel }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BannerLabelComponents = {
|
||||||
|
unreleased: UnreleasedVersionLabel,
|
||||||
|
unmaintained: UnmaintainedVersionLabel,
|
||||||
|
};
|
||||||
|
|
||||||
|
function BannerLabel(props) {
|
||||||
|
const BannerLabelComponent =
|
||||||
|
BannerLabelComponents[props.versionMetadata.banner];
|
||||||
|
return <BannerLabelComponent {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
function LatestVersionSuggestionLabel({ versionLabel, to, onClick }) {
|
function LatestVersionSuggestionLabel({ versionLabel, to, onClick }) {
|
||||||
return (
|
return (
|
||||||
<Translate
|
<Translate
|
||||||
id="theme.docs.versions.latestVersionSuggestionLabel"
|
id="theme.docs.versions.latestVersionSuggestionLabel"
|
||||||
description="The label userd to tell the user that he's browsing an unmaintained doc version"
|
description="The label used to tell the user to check the latest version"
|
||||||
values={{
|
values={{
|
||||||
versionLabel,
|
versionLabel,
|
||||||
latestVersionLink: (
|
latestVersionLink: (
|
||||||
<strong>
|
<b>
|
||||||
<Link to={to} onClick={onClick}>
|
<Link to={to} onClick={onClick}>
|
||||||
<Translate
|
<Translate
|
||||||
id="theme.docs.versions.latestVersionLinkLabel"
|
id="theme.docs.versions.latestVersionLinkLabel"
|
||||||
@ -74,7 +81,7 @@ function LatestVersionSuggestionLabel({ versionLabel, to, onClick }) {
|
|||||||
latest version
|
latest version
|
||||||
</Translate>
|
</Translate>
|
||||||
</Link>
|
</Link>
|
||||||
</strong>
|
</b>
|
||||||
),
|
),
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
@ -84,26 +91,22 @@ function LatestVersionSuggestionLabel({ versionLabel, to, onClick }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getVersionMainDoc = (version) =>
|
function DocVersionBannerEnabled({ versionMetadata }) {
|
||||||
version.docs.find((doc) => doc.id === version.mainDocId);
|
|
||||||
|
|
||||||
function DocVersionSuggestions() {
|
|
||||||
const {
|
const {
|
||||||
siteConfig: { title: siteTitle },
|
siteConfig: { title: siteTitle },
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
const { pluginId } = useActivePlugin({
|
const { pluginId } = useActivePlugin({
|
||||||
failfast: true,
|
failfast: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getVersionMainDoc = (version) =>
|
||||||
|
version.docs.find((doc) => doc.id === version.mainDocId);
|
||||||
|
|
||||||
const { savePreferredVersionName } = useDocsPreferredVersion(pluginId);
|
const { savePreferredVersionName } = useDocsPreferredVersion(pluginId);
|
||||||
const activeVersion = useActiveVersion(pluginId);
|
|
||||||
const {
|
const {
|
||||||
latestDocSuggestion,
|
latestDocSuggestion,
|
||||||
latestVersionSuggestion,
|
latestVersionSuggestion,
|
||||||
} = useDocVersionSuggestions(pluginId); // No suggestion to be made
|
} = useDocVersionSuggestions(pluginId); // try to link to same doc in latest version (not always possible)
|
||||||
|
|
||||||
if (!latestVersionSuggestion) {
|
|
||||||
return <></>;
|
|
||||||
} // try to link to same doc in latest version (not always possible)
|
|
||||||
// fallback to main doc of latest version
|
// fallback to main doc of latest version
|
||||||
|
|
||||||
const latestVersionSuggestedDoc =
|
const latestVersionSuggestedDoc =
|
||||||
@ -111,21 +114,25 @@ function DocVersionSuggestions() {
|
|||||||
return (
|
return (
|
||||||
<div className="alert alert--warning margin-bottom--md" role="alert">
|
<div className="alert alert--warning margin-bottom--md" role="alert">
|
||||||
<div>
|
<div>
|
||||||
{activeVersion.name === 'current' ? (
|
<BannerLabel siteTitle={siteTitle} versionMetadata={versionMetadata} />
|
||||||
<UnreleasedVersionLabel
|
|
||||||
siteTitle={siteTitle}
|
|
||||||
versionLabel={activeVersion.label}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<UnmaintainedVersionLabel
|
|
||||||
siteTitle={siteTitle}
|
|
||||||
versionLabel={activeVersion.label}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
{/* <div className="margin-top--md">
|
||||||
|
<LatestVersionSuggestionLabel
|
||||||
|
versionLabel={latestVersionSuggestion.label}
|
||||||
|
to={latestVersionSuggestedDoc.path}
|
||||||
|
onClick={() => savePreferredVersionName(latestVersionSuggestion.name)}
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DocVersionSuggestions;
|
function DocVersionBanner({ versionMetadata }) {
|
||||||
|
if (versionMetadata.banner === 'none') {
|
||||||
|
return <></>;
|
||||||
|
} else {
|
||||||
|
return <DocVersionBannerEnabled versionMetadata={versionMetadata} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocVersionBanner;
|
||||||
Loading…
Reference in New Issue
Block a user