问题:用户在拖拽直线端点时遇到运行时错误
错误信息:
imageData.getScalarData is not a function
TypeError: imageData.getScalarData is not a function
at LineGrayscaleMeasurementTool._samplePixelsAlongLine (line 690)
复现步骤:
LineGrayscaleMeasurementTool.ts:661
// ❌ 错误:viewport没有getImageData()方法
const imageData = (viewport as any).getImageData();
LineGrayscaleMeasurementTool.ts:690
// ❌ 错误:getScalarData()方法不存在
const scalarData = imageData.getScalarData();
const dimensions = imageData.getDimensions();
const spacing = imageData.getSpacing();
viewport.getImageData() 不是Cornerstone3D的标准APIgetScalarData()方法项目中已有正确实现:DicomOverlayTool.ts:80
import * as cornerstone from '@cornerstonejs/core';
// ✅ 正确:从缓存获取image对象
const image = cornerstone.cache.getImage(imageId);
const pixelData = image.getPixelData();
使用Cornerstone3D的缓存系统访问像素数据,而不是直接从viewport获取。
文件:src/components/measures/LineGrayscaleMeasurementTool.ts
修改:在文件开头添加导入
import * as cornerstone from '@cornerstonejs/core';
_updateCachedStats 方法位置:line 649-679
修改前:
private _updateCachedStats(
annotation: LineGrayscaleAnnotation,
enabledElement: CoreTypes.IEnabledElement
): void {
const { viewport } = enabledElement;
const { points } = annotation.data.handles;
if (points.length < 2) {
return;
}
// ❌ 错误的API
const imageData = (viewport as any).getImageData();
if (!imageData) {
console.warn('[LineGrayscaleTool] Image data not available');
return;
}
// ❌ 错误的方法调用
const sampleResult = this._samplePixelsAlongLine(points[0], points[1], imageData, viewport);
// ...
}
修改后:
private _updateCachedStats(
annotation: LineGrayscaleAnnotation,
enabledElement: CoreTypes.IEnabledElement
): void {
const { viewport } = enabledElement;
const { points } = annotation.data.handles;
if (points.length < 2) {
return;
}
// ✅ 使用Cornerstone缓存系统获取图像
const imageId = viewport.getCurrentImageId?.();
if (!imageId) {
console.warn('[LineGrayscaleTool] No imageId available');
return;
}
const image = cornerstone.cache.getImage(imageId);
if (!image) {
console.warn('[LineGrayscaleTool] Image not found in cache');
return;
}
// ✅ 传入image对象而非imageData
const sampleResult = this._samplePixelsAlongLine(points[0], points[1], image, viewport);
// 计算统计值
const stats = this._calculateGrayscaleStats(sampleResult.values, points[0], points[1]);
// 更新缓存
const targetId = `imageId:${imageId}`;
if (!annotation.data.cachedStats) {
annotation.data.cachedStats = {};
}
annotation.data.cachedStats[targetId] = stats;
}
_samplePixelsAlongLine 方法位置:line 684-741
修改前:
private _samplePixelsAlongLine(
startWorld: CoreTypes.Point3,
endWorld: CoreTypes.Point3,
imageData: any, // ❌ 错误的参数类型
viewport: CoreTypes.IStackViewport | CoreTypes.IVolumeViewport
): PixelSampleResult {
// ❌ 错误的方法调用
const scalarData = imageData.getScalarData();
const dimensions = imageData.getDimensions();
const spacing = imageData.getSpacing();
// ❌ 错误的坐标转换逻辑
const imageMetadata = (viewport as any).getImageData();
const origin = imageMetadata.origin || [0, 0, 0];
const startPixel = [
Math.floor((startWorld[0] - origin[0]) / spacing[0]),
Math.floor((startWorld[1] - origin[1]) / spacing[1]),
];
// ...
}
修改后:
private _samplePixelsAlongLine(
startWorld: CoreTypes.Point3,
endWorld: CoreTypes.Point3,
image: any, // ✅ 传入Cornerstone的image对象
viewport: CoreTypes.IStackViewport | CoreTypes.IVolumeViewport
): PixelSampleResult {
// ✅ 使用正确的API获取像素数据
const pixelData = image.getPixelData();
const { width, height } = image;
// ✅ 使用viewport坐标转换(世界坐标→Canvas坐标→像素坐标)
const startCanvas = viewport.worldToCanvas(startWorld);
const endCanvas = viewport.worldToCanvas(endWorld);
// ✅ Canvas坐标直接对应像素坐标(对于2D视图)
const startPixel = [
Math.floor(startCanvas[0]),
Math.floor(startCanvas[1]),
];
const endPixel = [
Math.floor(endCanvas[0]),
Math.floor(endCanvas[1]),
];
// Bresenham直线算法
const pixels = this._bresenhamLine(
startPixel[0],
startPixel[1],
endPixel[0],
endPixel[1]
);
// 采样灰度值
const values: number[] = [];
const coordinates: Array<{ x: number; y: number }> = [];
for (const pixel of pixels) {
const { x, y } = pixel;
// 边界检查
if (x < 0 || x >= width || y < 0 || y >= height) {
continue;
}
// ✅ 计算像素索引(行优先)
const index = y * width + x;
const value = pixelData[index];
values.push(value);
coordinates.push({ x, y });
}
return {
values,
coordinates,
count: values.length,
};
}
graph TB
A[Viewport] -->|getCurrentImageId| B[imageId]
B -->|cornerstone.cache.getImage| C[Image对象]
C -->|getPixelData| D[像素数组]
C -->|width, height| E[图像尺寸]
graph LR
A[世界坐标<br/>Point3] -->|viewport.worldToCanvas| B[Canvas坐标<br/>Point2]
B -->|直接对应| C[像素坐标<br/>x,y]
C -->|y*width+x| D[像素索引]
| 错误用法 ❌ | 正确用法 ✅ | 说明 |
|---|---|---|
viewport.getImageData() |
cornerstone.cache.getImage(imageId) |
获取图像对象 |
imageData.getScalarData() |
image.getPixelData() |
获取像素数组 |
imageData.getDimensions() |
image.width, image.height |
获取尺寸 |
| 手动计算世界坐标→像素坐标 | viewport.worldToCanvas() |
坐标转换 |
sequenceDiagram
participant User as 用户
participant Tool as LineGrayscaleTool
participant VP as Viewport
User->>Tool: 拖拽端点
Tool->>Tool: _mouseDragModifyCallback
Tool->>Tool: _updateCachedStats
Tool->>VP: viewport.getImageData() ❌
VP-->>Tool: undefined/错误对象
Tool->>Tool: imageData.getScalarData() ❌
Note over Tool: TypeError抛出
Tool--xUser: 功能失败
sequenceDiagram
participant User as 用户
participant Tool as LineGrayscaleTool
participant VP as Viewport
participant Cache as Cornerstone Cache
User->>Tool: 拖拽端点
Tool->>Tool: _mouseDragModifyCallback
Tool->>Tool: _updateCachedStats
Tool->>VP: getCurrentImageId()
VP-->>Tool: imageId
Tool->>Cache: cornerstone.cache.getImage(imageId) ✅
Cache-->>Tool: Image对象
Tool->>Tool: image.getPixelData() ✅
Tool->>Tool: _samplePixelsAlongLine
Tool->>Tool: _calculateGrayscaleStats
Tool->>Tool: 更新cachedStats
Tool-->>User: 显示统计结果
import * as cornerstone from '@cornerstonejs/core'_updateCachedStats 方法使用 cornerstone.cache.getImage()_samplePixelsAlongLine 方法参数从 imageData 改为 imagegetScalarData() 改为 getPixelData()getDimensions() 改为 image.width/heightviewport.worldToCanvas()spacing 和 origin 计算修复后,用户应该能够:
Cornerstone3D 缓存系统文档
https://www.cornerstonejs.org/docs/concepts/cornerstone-core/cache
项目内正确实现示例
DicomOverlayTool.ts:80
原设计文档
docs/实现/直线灰度测量功能设计.md
文档版本:1.0
创建日期:2025-12-10
最后更新:2025-12-10
状态:待实施