diff --git a/extensions/cornerstone-dicom-sr/src/commandsModule.ts b/extensions/cornerstone-dicom-sr/src/commandsModule.ts
index f31f783f03..e8aab79b31 100644
--- a/extensions/cornerstone-dicom-sr/src/commandsModule.ts
+++ b/extensions/cornerstone-dicom-sr/src/commandsModule.ts
@@ -1,8 +1,9 @@
 import { metaData, utilities } from '@cornerstonejs/core';
 
-import OHIF, { DicomMetadataStore } from '@ohif/core';
+import OHIF, { DicomMetadataStore, utils } from '@ohif/core';
 import dcmjs from 'dcmjs';
 import { adaptersSR } from '@cornerstonejs/adapters';
+import { showLabelAnnotationPopup, colorPickerDialog } from '@ohif/extension-default';
 
 import getFilteredCornerstoneToolState from './utils/getFilteredCornerstoneToolState';
 import hydrateStructuredReport from './utils/hydrateStructuredReport';
@@ -11,7 +12,6 @@ const { MeasurementReport } = adaptersSR.Cornerstone3D;
 const { log } = OHIF;
 
 /**
- *
  * @param measurementData An array of measurements from the measurements service
  * that you wish to serialize.
  * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
@@ -43,8 +43,30 @@ const _generateReport = (measurementData, additionalFindingTypes, options = {})
 
 const commandsModule = (props: withAppTypes) => {
   const { servicesManager, extensionManager, commandsManager } = props;
-  const { customizationService, displaySetService, viewportGridService } = servicesManager.services;
+  const { customizationService, measurementService, viewportGridService, uiDialogService } =
+    servicesManager.services;
+
   const actions = {
+    changeColorMeasurement: ({ uid }) => {
+      // When this gets supported, it probably belongs in cornerstone, not sr
+      throw new Error('Unsupported operation: changeColorMeasurement');
+      // const { color } = measurementService.getMeasurement(uid);
+      // const rgbaColor = {
+      //   r: color[0],
+      //   g: color[1],
+      //   b: color[2],
+      //   a: color[3] / 255.0,
+      // };
+      // colorPickerDialog(uiDialogService, rgbaColor, (newRgbaColor, actionId) => {
+      //   if (actionId === 'cancel') {
+      //     return;
+      //   }
+
+      //   const color = [newRgbaColor.r, newRgbaColor.g, newRgbaColor.b, newRgbaColor.a * 255.0];
+      // segmentationService.setSegmentColor(viewportId, segmentationId, segmentIndex, color);
+      // });
+    },
+
     /**
      *
      * @param measurementData An array of measurements from the measurements service
diff --git a/extensions/cornerstone/src/commandsModule.ts b/extensions/cornerstone/src/commandsModule.ts
index 89e498904a..229281ee21 100644
--- a/extensions/cornerstone/src/commandsModule.ts
+++ b/extensions/cornerstone/src/commandsModule.ts
@@ -14,7 +14,7 @@ import {
   annotation,
 } from '@cornerstonejs/tools';
 
-import { Types as OhifTypes } from '@ohif/core';
+import { Types as OhifTypes, utils } from '@ohif/core';
 import i18n from '@ohif/i18n';
 import {
   callLabelAutocompleteDialog,
@@ -295,6 +295,60 @@ function commandsModule({
       measurementService.update(updatedMeasurement.uid, updatedMeasurement, true);
     },
 
+    /**
+     * Jumps to the specified (by uid) measurement in the active viewport.
+     * Also marks any provided display measurements isActive value
+     */
+    jumpToMeasurement: ({ uid, displayMeasurements = [] }) => {
+      measurementService.jumpToMeasurement(viewportGridService.getActiveViewportId(), uid);
+      for (const measurement of displayMeasurements) {
+        measurement.isActive = measurement.uid === uid;
+      }
+    },
+
+    removeMeasurement: ({ uid }) => {
+      measurementService.remove(uid);
+    },
+
+    renameMeasurement: ({ uid }) => {
+      const labelConfig = customizationService.get('measurementLabels');
+      const measurement = measurementService.getMeasurement(uid);
+      showLabelAnnotationPopup(measurement, uiDialogService, labelConfig).then(val => {
+        measurementService.update(
+          uid,
+          {
+            ...val,
+          },
+          true
+        );
+      });
+    },
+
+    toggleLockMeasurement: ({ uid }) => {
+      measurementService.toggleLockMeasurement(uid);
+    },
+
+    toggleVisibilityMeasurement: ({ uid }) => {
+      measurementService.toggleVisibilityMeasurement(uid);
+    },
+
+    /**
+     * Clear the measurements
+     */
+    clearMeasurements: options => {
+      const { measurementFilter } = options;
+      measurementService.clearMeasurements(
+        measurementFilter ? measurementFilter.bind(options) : null
+      );
+    },
+
+    /**
+     * Download the CSV report for the measurements.
+     */
+    downloadCSVMeasurementsReport: ({ measurementFilter }) => {
+      utils.downloadCSVReport(measurementService.getMeasurements(measurementFilter));
+    },
+
     // Retrieve value commands
     getActiveViewportEnabledElement: _getActiveViewportEnabledElement,
 
@@ -1281,6 +1335,27 @@ function commandsModule({
     updateMeasurement: {
       commandFn: actions.updateMeasurement,
     },
+    clearMeasurements: {
+      commandFn: actions.clearMeasurements,
+    },
+    jumpToMeasurement: {
+      commandFn: actions.jumpToMeasurement,
+    },
+    removeMeasurement: {
+      commandFn: actions.removeMeasurement,
+    },
+    renameMeasurement: {
+      commandFn: actions.renameMeasurement,
+    },
+    toggleLockMeasurement: {
+      commandFn: actions.toggleLockMeasurement,
+    },
+    toggleVisibilityMeasurement: {
+      commandFn: actions.toggleVisibilityMeasurement,
+    },
+    downloadCSVMeasurementsReport: {
+      commandFn: actions.downloadCSVMeasurementsReport,
+    },
     setViewportWindowLevel: {
       commandFn: actions.setViewportWindowLevel,
     },
diff --git a/extensions/cornerstone/src/components/StudySummaryFromMetadata.tsx b/extensions/cornerstone/src/components/StudySummaryFromMetadata.tsx
new file mode 100644
index 0000000000..60d8b94d51
--- /dev/null
+++ b/extensions/cornerstone/src/components/StudySummaryFromMetadata.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { DicomMetadataStore, utils } from '@ohif/core';
+import { StudySummary } from '@ohif/ui-next';
+
+const { formatDate } = utils;
+
+export function StudySummaryFromMetadata({ StudyInstanceUID }) {
+  if (!StudyInstanceUID) {
+    return null;
+  }
+  const studyMeta = DicomMetadataStore.getStudy(StudyInstanceUID);
+  if (!studyMeta?.series?.length) {
+    return null;
+  }
+
+  const instanceMeta = studyMeta.series[0].instances[0];
+  const { StudyDate, StudyDescription } = instanceMeta;
+
+  return (
+    <StudySummary
+      date={formatDate(StudyDate)}
+      description={StudyDescription}
+    ></StudySummary>
+  );
+}
diff --git a/extensions/cornerstone/src/getPanelModule.tsx b/extensions/cornerstone/src/getPanelModule.tsx
index bb3f65d9d8..20c3a43e7b 100644
--- a/extensions/cornerstone/src/getPanelModule.tsx
+++ b/extensions/cornerstone/src/getPanelModule.tsx
@@ -3,7 +3,7 @@ import React from 'react';
 import { Toolbox } from '@ohif/ui-next';
 import PanelSegmentation from './panels/PanelSegmentation';
 import ActiveViewportWindowLevel from './components/ActiveViewportWindowLevel';
-import PanelMeasurementTable from './panels/PanelMeasurement';
+import PanelMeasurement from './panels/PanelMeasurement';
 
 const getPanelModule = ({ commandsManager, servicesManager, extensionManager }: withAppTypes) => {
   const wrappedPanelSegmentation = ({ configuration }) => {
@@ -59,7 +59,7 @@ const getPanelModule = ({ commandsManager, servicesManager, extensionManager }:
 
   const wrappedPanelMeasurement = ({ configuration }) => {
     return (
-      <PanelMeasurementTable
+      <PanelMeasurement
         commandsManager={commandsManager}
         servicesManager={servicesManager}
         extensionManager={extensionManager}
diff --git a/extensions/cornerstone/src/hooks/useMeasurements.ts b/extensions/cornerstone/src/hooks/useMeasurements.ts
index ba659a35a5..e76445375d 100644
--- a/extensions/cornerstone/src/hooks/useMeasurements.ts
+++ b/extensions/cornerstone/src/hooks/useMeasurements.ts
@@ -62,10 +62,7 @@ export function useMeasurements(servicesManager, { measurementFilter }) {
 
   useEffect(() => {
     const updateDisplayMeasurements = () => {
-      let measurements = measurementService.getMeasurements();
-      if (measurementFilter) {
-        measurements = measurements.filter(measurementFilter);
-      }
+      let measurements = measurementService.getMeasurements(measurementFilter);
       const mappedMeasurements = measurements.map(m =>
         mapMeasurementToDisplay(m, displaySetService)
       );
diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx
index 41f4c33ff0..f5204e82e8 100644
--- a/extensions/cornerstone/src/index.tsx
+++ b/extensions/cornerstone/src/index.tsx
@@ -53,6 +53,7 @@ import PanelSegmentation from './panels/PanelSegmentation';
 import PanelMeasurement from './panels/PanelMeasurement';
 import DicomUpload from './components/DicomUpload/DicomUpload';
 import { useSegmentations } from './hooks/useSegmentations';
+import { StudySummaryFromMetadata } from './components/StudySummaryFromMetadata';
 
 const { imageRetrieveMetadataProvider } = cornerstone.utilities;
 
@@ -254,5 +255,6 @@ export {
   PanelSegmentation,
   PanelMeasurement,
   DicomUpload,
+  StudySummaryFromMetadata,
 };
 export default cornerstoneExtension;
diff --git a/extensions/cornerstone/src/panels/PanelMeasurement.tsx b/extensions/cornerstone/src/panels/PanelMeasurement.tsx
index ed86b2ef7e..b2279d80a3 100644
--- a/extensions/cornerstone/src/panels/PanelMeasurement.tsx
+++ b/extensions/cornerstone/src/panels/PanelMeasurement.tsx
@@ -1,19 +1,29 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
+import { utils } from '@ohif/core';
 import { useViewportGrid } from '@ohif/ui-next';
 import { MeasurementTable } from '@ohif/ui-next';
 import debounce from 'lodash.debounce';
 import { useMeasurements } from '../hooks/useMeasurements';
-import { showLabelAnnotationPopup, colorPickerDialog } from '@ohif/extension-default';
 
-export default function PanelMeasurementTable({
+const {
+  filterAdditionalFindings: filterAdditionalFinding,
+  filterOr,
+  filterAny,
+} = utils.MeasurementFilters;
+
+export type withAppAndFilters = withAppTypes & {
+  measurementFilter: (item) => boolean;
+};
+
+export default function PanelMeasurement({
   servicesManager,
+  commandsManager,
   customHeader,
-  measurementFilter,
-}: withAppTypes): React.ReactNode {
+  measurementFilter = filterAny,
+}: withAppAndFilters): React.ReactNode {
   const measurementsPanelRef = useRef(null);
 
-  const [viewportGrid] = useViewportGrid();
-  const { measurementService, customizationService, uiDialogService } = servicesManager.services;
+  const { measurementService } = servicesManager.services;
 
   const displayMeasurements = useMeasurements(servicesManager, {
     measurementFilter,
@@ -27,75 +37,37 @@ export default function PanelMeasurementTable({
     }
   }, [displayMeasurements.length]);
 
-  const onMeasurementItemClickHandler = (uid: string, isActive: boolean) => {
-    if (isActive) {
-      return;
-    }
-
-    const measurements = [...displayMeasurements];
-    const measurement = measurements.find(m => m.uid === uid);
-
-    measurements.forEach(m => (m.isActive = m.uid !== uid ? false : true));
-    measurement.isActive = true;
-  };
-
-  const jumpToImage = (uid: string) => {
-    measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
-    onMeasurementItemClickHandler(uid, true);
-  };
-
-  const removeMeasurement = (uid: string) => {
-    measurementService.remove(uid);
-  };
-
-  const renameMeasurement = (uid: string) => {
-    jumpToImage(uid);
-    const labelConfig = customizationService.get('measurementLabels');
-    const measurement = measurementService.getMeasurement(uid);
-    showLabelAnnotationPopup(measurement, uiDialogService, labelConfig).then(val => {
-      measurementService.update(
-        uid,
-        {
-          ...val,
-        },
-        true
-      );
-    });
-  };
-
-  const changeColorMeasurement = (uid: string) => {
-    const { color } = measurementService.getMeasurement(uid);
-    const rgbaColor = {
-      r: color[0],
-      g: color[1],
-      b: color[2],
-      a: color[3] / 255.0,
+  const bindCommand = (name: string | string[], options?) => {
+    return (uid: string) => {
+      commandsManager.run(name, { ...options, uid });
     };
-    colorPickerDialog(uiDialogService, rgbaColor, (newRgbaColor, actionId) => {
-      if (actionId === 'cancel') {
-        return;
-      }
-
-      const color = [newRgbaColor.r, newRgbaColor.g, newRgbaColor.b, newRgbaColor.a * 255.0];
-      // segmentationService.setSegmentColor(viewportId, segmentationId, segmentIndex, color);
-    });
   };
 
-  const toggleLockMeasurement = (uid: string) => {
-    measurementService.toggleLockMeasurement(uid);
-  };
+  const jumpToImage = bindCommand('jumpToMeasurement', { displayMeasurements });
+  const removeMeasurement = bindCommand('removeMeasurement');
+  const renameMeasurement = bindCommand(['jumpToMeasurement', 'renameMeasurement'], {
+    displayMeasurements,
+  });
+  const toggleLockMeasurement = bindCommand('toggleLockMeasurement');
+  const toggleVisibilityMeasurement = bindCommand('toggleVisibilityMeasurement');
 
-  const toggleVisibilityMeasurement = (uid: string) => {
-    measurementService.toggleVisibilityMeasurement(uid);
-  };
+  const additionalFilter = filterAdditionalFinding(measurementService);
 
   const measurements = displayMeasurements.filter(
-    dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT && dm.referencedImageId
+    item => !additionalFilter(item) && measurementFilter(item)
   );
   const additionalFindings = displayMeasurements.filter(
-    dm => dm.measurementType === measurementService.VALUE_TYPES.POINT && dm.referencedImageId
+    item => additionalFilter(item) && measurementFilter(item)
   );
 
+  const onArgs = {
+    onClick: jumpToImage,
+    onDelete: removeMeasurement,
+    onToggleVisibility: toggleVisibilityMeasurement,
+    onToggleLocked: toggleLockMeasurement,
+    onRename: renameMeasurement,
+  };
+
   return (
     <>
       <div
@@ -104,13 +76,10 @@ export default function PanelMeasurementTable({
         data-cy={'trackedMeasurements-panel'}
       >
         <MeasurementTable
+          key="tracked"
           title="Measurements"
           data={measurements}
-          onClick={jumpToImage}
-          onDelete={removeMeasurement}
-          onToggleVisibility={toggleVisibilityMeasurement}
-          onToggleLocked={toggleLockMeasurement}
-          onRename={renameMeasurement}
+          {...onArgs}
           // onColor={changeColorMeasurement}
         >
           <MeasurementTable.Header>
@@ -129,14 +98,10 @@ export default function PanelMeasurementTable({
         </MeasurementTable>
         {additionalFindings.length > 0 && (
           <MeasurementTable
+            key="additional"
             data={additionalFindings}
             title="Additional Findings"
-            onClick={jumpToImage}
-            onDelete={removeMeasurement}
-            onToggleVisibility={toggleVisibilityMeasurement}
-            onToggleLocked={toggleLockMeasurement}
-            onRename={renameMeasurement}
-            // onColor={changeColorMeasurement}
+            {...onArgs}
           >
             <MeasurementTable.Body />
           </MeasurementTable>
diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts
index bac5b4a2ae..8f3b96952b 100644
--- a/extensions/default/src/commandsModule.ts
+++ b/extensions/default/src/commandsModule.ts
@@ -562,27 +562,13 @@ const commandsModule = ({
   };
 
   const definitions = {
-    showContextMenu: {
-      commandFn: actions.showContextMenu,
-    },
-    closeContextMenu: {
-      commandFn: actions.closeContextMenu,
-    },
-    clearMeasurements: {
-      commandFn: actions.clearMeasurements,
-    },
-    displayNotification: {
-      commandFn: actions.displayNotification,
-    },
-    setHangingProtocol: {
-      commandFn: actions.setHangingProtocol,
-    },
-    toggleHangingProtocol: {
-      commandFn: actions.toggleHangingProtocol,
-    },
-    navigateHistory: {
-      commandFn: actions.navigateHistory,
-    },
+    showContextMenu: actions.showContextMenu,
+    closeContextMenu: actions.closeContextMenu,
+    clearMeasurements: actions.clearMeasurements,
+    displayNotification: actions.displayNotification,
+    setHangingProtocol: actions.setHangingProtocol,
+    toggleHangingProtocol: actions.toggleHangingProtocol,
+    navigateHistory: actions.navigateHistory,
     nextStage: {
       commandFn: actions.deltaStage,
       options: { direction: 1 },
@@ -591,18 +577,10 @@ const commandsModule = ({
       commandFn: actions.deltaStage,
       options: { direction: -1 },
     },
-    setViewportGridLayout: {
-      commandFn: actions.setViewportGridLayout,
-    },
-    toggleOneUp: {
-      commandFn: actions.toggleOneUp,
-    },
-    openDICOMTagViewer: {
-      commandFn: actions.openDICOMTagViewer,
-    },
-    updateViewportDisplaySet: {
-      commandFn: actions.updateViewportDisplaySet,
-    },
+    setViewportGridLayout: actions.setViewportGridLayout,
+    toggleOneUp: actions.toggleOneUp,
+    openDICOMTagViewer: actions.openDICOMTagViewer,
+    updateViewportDisplaySet: actions.updateViewportDisplaySet,
   };
 
   return {
diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
index 147e9c6512..f33a1ce968 100644
--- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
+++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx
@@ -1,20 +1,12 @@
 import React, { useEffect, useState } from 'react';
-import { PanelMeasurement } from '@ohif/extension-cornerstone';
+import { DicomMetadataStore, utils } from '@ohif/core';
 import { useViewportGrid } from '@ohif/ui-next';
-import { StudySummary } from '@ohif/ui-next';
 import { Button, Icons } from '@ohif/ui-next';
-import { DicomMetadataStore, utils } from '@ohif/core';
+import { PanelMeasurement, StudySummaryFromMetadata } from '@ohif/extension-cornerstone';
 import { useTrackedMeasurements } from '../getContextModule';
-import { useTranslation } from 'react-i18next';
 
-const { downloadCSVReport, formatDate } = utils;
-
-const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = {
-  key: undefined, //
-  date: '', // '07-Sep-2010',
-  modality: '', // 'CT',
-  description: '', // 'CHEST/ABD/PELVIS W CONTRAST',
-};
+const { filterAnd, filterPlanarMeasurement, filterAny, filterMeasurementsBySeriesUID } =
+  utils.MeasurementFilters;
 
 function PanelMeasurementTableTracking({
   servicesManager,
@@ -22,52 +14,12 @@ function PanelMeasurementTableTracking({
   commandsManager,
 }: withAppTypes) {
   const [viewportGrid] = useViewportGrid();
-  const { t } = useTranslation('MeasurementTable');
-  const { measurementService, customizationService } = servicesManager.services;
+  const { customizationService } = servicesManager.services;
   const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements();
   const { trackedStudy, trackedSeries } = trackedMeasurements.context;
-  const [displayStudySummary, setDisplayStudySummary] = useState(
-    DISPLAY_STUDY_SUMMARY_INITIAL_VALUE
-  );
-
-  useEffect(() => {
-    const updateDisplayStudySummary = async () => {
-      if (trackedMeasurements.matches('tracking') && trackedStudy) {
-        const studyMeta = DicomMetadataStore.getStudy(trackedStudy);
-        if (!studyMeta || !studyMeta.series || studyMeta.series.length === 0) {
-          console.debug('Study metadata not available');
-          return;
-        }
-
-        const instanceMeta = studyMeta.series[0].instances[0];
-        const { StudyDate, StudyDescription } = instanceMeta;
-
-        const modalities = new Set();
-        studyMeta.series.forEach(series => {
-          if (trackedSeries.includes(series.SeriesInstanceUID)) {
-            modalities.add(series.instances[0].Modality);
-          }
-        });
-        const modality = Array.from(modalities).join('/');
-
-        setDisplayStudySummary(prevSummary => {
-          if (prevSummary.key !== trackedStudy) {
-            return {
-              key: trackedStudy,
-              date: StudyDate,
-              modality,
-              description: StudyDescription,
-            };
-          }
-          return prevSummary;
-        });
-      } else if (!trackedStudy) {
-        setDisplayStudySummary(DISPLAY_STUDY_SUMMARY_INITIAL_VALUE);
-      }
-    };
-
-    updateDisplayStudySummary();
-  }, [trackedMeasurements, trackedStudy, trackedSeries]);
+  const measurementFilter = trackedStudy
+    ? filterAnd(filterPlanarMeasurement, filterMeasurementsBySeriesUID(trackedSeries))
+    : filterPlanarMeasurement;
 
   const { disableEditing } = customizationService.getCustomization(
     'PanelMeasurement.disableEditing',
@@ -79,20 +31,12 @@ function PanelMeasurementTableTracking({
 
   return (
     <>
-      {displayStudySummary.key && (
-        <StudySummary
-          date={formatDate(displayStudySummary.date)}
-          description={displayStudySummary.description}
-        />
-      )}
+      <StudySummaryFromMetadata StudyInstanceUID={trackedStudy} />
       <PanelMeasurement
         servicesManager={servicesManager}
         extensionManager={extensionManager}
         commandsManager={commandsManager}
-        measurementFilter={measurement =>
-          trackedStudy === measurement.referenceStudyUID &&
-          trackedSeries.includes(measurement.referenceSeriesUID)
-        }
+        measurementFilter={measurementFilter}
         customHeader={({ additionalFindings, measurements }) => {
           const disabled = additionalFindings.length === 0 && measurements.length === 0;
 
@@ -108,14 +52,9 @@ function PanelMeasurementTableTracking({
                   variant="ghost"
                   className="pl-1.5"
                   onClick={() => {
-                    const measurements = measurementService.getMeasurements();
-                    const trackedMeasurements = measurements.filter(
-                      m =>
-                        trackedStudy === m.referenceStudyUID &&
-                        trackedSeries.includes(m.referenceSeriesUID)
-                    );
-
-                    downloadCSVReport(trackedMeasurements);
+                    commandsManager.runCommand('downloadCSVMeasurementsReport', {
+                      measurementFilter,
+                    });
                   }}
                 >
                   <Icons.Download className="h-5 w-5" />
@@ -140,7 +79,7 @@ function PanelMeasurementTableTracking({
                   variant="ghost"
                   className="pl-0.5"
                   onClick={() => {
-                    measurementService.clearMeasurements();
+                    commandsManager.runCommand('clearMeasurements', { measurementFilter });
                   }}
                 >
                   <Icons.Delete />
diff --git a/modes/basic-test-mode/src/index.ts b/modes/basic-test-mode/src/index.ts
index 740ff90796..41d9d63413 100644
--- a/modes/basic-test-mode/src/index.ts
+++ b/modes/basic-test-mode/src/index.ts
@@ -15,7 +15,6 @@ const ohif = {
   wsiSopClassHandler:
     '@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler',
   thumbnailList: '@ohif/extension-default.panelModule.seriesList',
-  measurements: '@ohif/extension-default.panelModule.measurements',
 };
 
 const tracked = {
@@ -46,6 +45,7 @@ const dicomSeg = {
 
 const cornerstone = {
   panel: '@ohif/extension-cornerstone.panelModule.panelSegmentation',
+  measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement',
 };
 
 const dicomPmap = {
@@ -150,7 +150,8 @@ function modeFactory() {
               // leftPanels: [ohif.thumbnailList],
               // rightPanels: [dicomSeg.panel, ohif.measurements],
               leftPanels: [tracked.thumbnailList],
-              rightPanels: [cornerstone.panel, tracked.measurements],
+              // Can use cornerstone.measurements for all measurements
+              rightPanels: [cornerstone.panel, tracked.measurements, cornerstone.measurements],
               // rightPanelClosed: true, // optional prop to start with collapse panels
               viewports: [
                 {
diff --git a/platform/core/src/classes/CommandsManager.ts b/platform/core/src/classes/CommandsManager.ts
index 0138521f90..096054da80 100644
--- a/platform/core/src/classes/CommandsManager.ts
+++ b/platform/core/src/classes/CommandsManager.ts
@@ -161,6 +161,16 @@ export class CommandsManager {
    * Run one or more commands with specified extra options.
    * Returns the result of the last command run.
    *
+   * Example commands to run are:
+   * * 'updateMeasurement'
+   * * `{commandName: 'displayWhatever'}`
+   * * `['updateMeasurement', {commandName: 'displayWhatever'}]`
+   * * `{ commands: 'updateMeasurement' }`
+   * * `{ commands: ['updateMeasurement', {commandName: 'displayWhatever'}]}`
+   *
+   * Note how the various styles can be mixed, simplifying the declaration of
+   * sets of commands.
+   *
    * @param toRun - A specification of one or more commands,
    *  typically an object of { commandName, commandOptions, context }
    * or an array of such objects. It can also be a single commandName as string
@@ -169,7 +179,7 @@ export class CommandsManager {
    *   the commandOptions specified in the base.
    */
   public run(
-    toRun: Command | Commands | Command[] | string | undefined,
+    toRun: Command | Commands | (Command | string)[] | string | undefined,
     options?: Record<string, unknown>
   ): unknown {
     if (!toRun) {
diff --git a/platform/core/src/extensions/ExtensionManager.ts b/platform/core/src/extensions/ExtensionManager.ts
index 588695d107..af620b535a 100644
--- a/platform/core/src/extensions/ExtensionManager.ts
+++ b/platform/core/src/extensions/ExtensionManager.ts
@@ -597,7 +597,10 @@ export default class ExtensionManager extends PubSubService {
     }
 
     Object.keys(definitions).forEach(commandName => {
-      const commandDefinition = definitions[commandName];
+      let commandDefinition = definitions[commandName];
+      if (typeof commandDefinition === 'function') {
+        commandDefinition = { commandFn: commandDefinition };
+      }
       const commandHasContextThatDoesNotExist =
         commandDefinition.context && !this._commandsManager.getContext(commandDefinition.context);
 
diff --git a/platform/core/src/services/MeasurementService/MeasurementService.ts b/platform/core/src/services/MeasurementService/MeasurementService.ts
index 4f823c9563..0fa3bf7c82 100644
--- a/platform/core/src/services/MeasurementService/MeasurementService.ts
+++ b/platform/core/src/services/MeasurementService/MeasurementService.ts
@@ -92,6 +92,8 @@ const VALUE_TYPES = {
   ROI_THRESHOLD_MANUAL: 'value_type::roiThresholdManual',
 };
 
+export type MeasurementFilter = (measurement) => boolean;
+
 /**
  * MeasurementService class that supports source management and measurement management.
  * Sources can be any library that can provide "annotations" (e.g. cornerstone-tools, cornerstone, etc.)
@@ -108,7 +110,7 @@ class MeasurementService extends PubSubService {
   public static REGISTRATION = {
     name: 'measurementService',
     altName: 'MeasurementService',
-    create: ({ configuration = {} }) => {
+    create: _options => {
       return new MeasurementService();
     },
   };
@@ -120,10 +122,11 @@ class MeasurementService extends PubSubService {
   private measurements = new Map();
   private unmappedMeasurements = new Map();
 
+  private sources = {};
+  private mappings = {};
+
   constructor() {
     super(EVENTS);
-    this.sources = {};
-    this.mappings = {};
   }
 
   /**
@@ -164,12 +167,16 @@ class MeasurementService extends PubSubService {
   }
 
   /**
-   * Get all measurements.
+   * Gets measurements, optionally filtered by the filter
+   * function.
    *
    * @return {Measurement[]} Array of measurements
    */
-  getMeasurements() {
-    return [...this.measurements.values()];
+  public getMeasurements(filter?: MeasurementFilter) {
+    const measurements = [...this.measurements.values()];
+    return filter
+      ? measurements.filter(measurement => filter.call(this, measurement))
+      : measurements;
   }
 
   /**
@@ -582,11 +589,20 @@ class MeasurementService extends PubSubService {
     });
   }
 
-  clearMeasurements() {
+  /**
+   * Clears measurements that match the filter, defaulting to all of them.
+   * That allows, for example, clearing all of a single studies measurements
+   * without needing to clear other measurements.
+   */
+  public clearMeasurements(filter?: MeasurementFilter) {
     // Make a copy of the measurements
-    const measurements = [...this.measurements.values(), ...this.unmappedMeasurements.values()];
-    this.unmappedMeasurements.clear();
-    this.measurements.clear();
+    const toClear = this.getMeasurements(filter);
+    const unmappedClear = filter
+      ? [...this.unmappedMeasurements.values()].filter(filter)
+      : this.unmappedMeasurements;
+    const measurements = [...toClear, ...unmappedClear];
+    unmappedClear.forEach(measurement => this.unmappedMeasurements.delete(measurement.uid));
+    toClear.forEach(measurement => this.measurements.delete(measurement.uid));
     this._broadcastEvent(this.EVENTS.MEASUREMENTS_CLEARED, { measurements });
   }
 
diff --git a/platform/core/src/utils/index.test.js b/platform/core/src/utils/index.test.js
index 85d18f71d5..9e7edfd85f 100644
--- a/platform/core/src/utils/index.test.js
+++ b/platform/core/src/utils/index.test.js
@@ -43,6 +43,7 @@ describe('Top level exports', () => {
       'progressTrackingUtils',
       'uuidv4',
       'addAccessors',
+      'MeasurementFilters',
     ].sort();
 
     const exports = Object.keys(utils.default).sort();
diff --git a/platform/core/src/utils/index.ts b/platform/core/src/utils/index.ts
index 74c9cdd381..e2e4d03818 100644
--- a/platform/core/src/utils/index.ts
+++ b/platform/core/src/utils/index.ts
@@ -39,6 +39,7 @@ import {
 import { splitComma, getSplitParam } from './splitComma';
 import { createStudyBrowserTabs } from './createStudyBrowserTabs';
 import { sopClassDictionary } from './sopClassDictionary';
+import * as MeasurementFilters from './measurementFilters';
 
 // Commented out unused functionality.
 // Need to implement new mechanism for derived displaySets using the displaySetManager.
@@ -84,6 +85,7 @@ const utils = {
   getSplitParam,
   generateAcceptHeader,
   createStudyBrowserTabs,
+  MeasurementFilters,
 };
 
 export {
@@ -117,6 +119,7 @@ export {
   getSplitParam,
   generateAcceptHeader,
   createStudyBrowserTabs,
+  MeasurementFilters,
 };
 
 export default utils;
diff --git a/platform/core/src/utils/measurementFilters.ts b/platform/core/src/utils/measurementFilters.ts
new file mode 100644
index 0000000000..db36a305c7
--- /dev/null
+++ b/platform/core/src/utils/measurementFilters.ts
@@ -0,0 +1,110 @@
+/**
+ * Returns a filter function which filters for measurements belonging to both
+ * the study and series.
+ */
+export function filterMeasurementsBySeriesUID(selectedSeries: string[]) {
+  return measurement => selectedSeries.includes(measurement.referenceSeriesUID);
+}
+
+/**
+ * @returns true for measurements include referencedImageId (coplanar with an image)
+ */
+export function filterPlanarMeasurement(measurement) {
+  return measurement?.referencedImageId;
+}
+
+/** A filter that always returns true */
+export function filterAny(_measurement) {
+  return true;
+}
+
+/** A filter that excludes everything */
+export function filterNone(_measurement) {
+  return false;
+}
+
+/**
+ *  Filters the measurements which are found in any of the specified
+ * filters.  Strings will be looked up by name.
+ */
+export function filterOr(...filters) {
+  return function (item) {
+    for (let filter of filters) {
+      if (typeof filter === 'string') {
+        filter = this[filter];
+      }
+      if (typeof filter !== 'function') {
+        continue;
+      }
+      if (filter.call(this, item)) {
+        return true;
+      }
+    }
+    return false;
+  };
+}
+
+/**
+ * Filters for additional findings, that is, measurements with
+ * a value of type point, and having a referenced image
+ */
+export function filterAdditionalFindings(measurementService) {
+  const { POINT } = measurementService.VALUE_TYPES;
+  return dm => dm.type === POINT && dm.referencedImageId;
+}
+
+/**
+ * Returns a filter that applies the second filter unless the first filter would
+ * include the given measurement.
+ * That is, (!filterUnless) && filterThen
+ */
+export function filterUnless(filterUnless, filterThen) {
+  return item => (filterUnless(item) ? false : filterThen(item));
+}
+
+const isString = s => typeof s === 'string' || s instanceof String;
+
+/**
+ * Returns true if all the filters return true.
+ * Any filter can be a string name of a filter on the "this" object
+ * called on the final filter call.
+ */
+export function filterAnd(...filters) {
+  return function (item) {
+    for (const filter of filters) {
+      if (isString(filter)) {
+        if (!this[filter](item)) {
+          return false;
+        }
+      } else if (!filter.call(this, item)) {
+        return false;
+      }
+    }
+    return true;
+  };
+}
+
+/**
+ * Returns a filter that returns true if none of the filters supplied return true.
+ * Any filter supplied can be a name, in which case hte filter will be retrieved
+ * from "this" object on the call.
+ *
+ * For example, for filterNot("otherFilterName"), if that is called on
+ * `{ otherFilterName: filterNone }`
+ * then otherFilterName will be called, returning false in this case and
+ * filterNot will return true.
+ *
+ *
+ */
+export function filterNot(...filters) {
+  if (filters.length !== 1) {
+    return filterAnd.apply(null, filters.map(filterNot));
+  }
+  const [filter] = filters;
+  if (isString(filter)) {
+    return function (item) {
+      return !this[filter](item);
+    };
+  }
+  return item => !filter(item);
+}