feat/vtk-3d-view #7
|
|
@ -177,17 +177,26 @@ bool captureAnomalyShotFromSlice(vtkImageData* colorImg, const double o[3], cons
|
|||
const QRect crop = shape.boundingRect().toAlignedRect().intersected(QRect(0, 0, nx, ny));
|
||||
if (crop.width() < 1 || crop.height() < 1) return false;
|
||||
|
||||
// 切片 RGB(vtk, j=0 在底) → QImage(顶左原点,翻行)。
|
||||
QImage src(nx, ny, QImage::Format_RGB888);
|
||||
// 切片着色图(vtk, j=0 在底) → QImage(顶左原点,翻行)。RGBA 保留外区透明(消除血缘外蓝边)。
|
||||
const int comps = colorImg->GetNumberOfScalarComponents();
|
||||
const bool rgba = comps >= 4;
|
||||
QImage src(nx, ny, rgba ? QImage::Format_RGBA8888 : QImage::Format_RGB888);
|
||||
for (int j = 0; j < ny; ++j) {
|
||||
uchar* row = src.scanLine(ny - 1 - j);
|
||||
for (int i = 0; i < nx; ++i) {
|
||||
const auto* px = static_cast<unsigned char*>(colorImg->GetScalarPointer(i, j, 0));
|
||||
if (rgba) {
|
||||
row[i * 4] = px[0];
|
||||
row[i * 4 + 1] = px[1];
|
||||
row[i * 4 + 2] = px[2];
|
||||
row[i * 4 + 3] = px[3];
|
||||
} else {
|
||||
row[i * 3] = px[0];
|
||||
row[i * 3 + 1] = px[1];
|
||||
row[i * 3 + 2] = px[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 输出:buffer 形状内贴剖面像素(外透明),再描异常轮廓。
|
||||
QImage out(crop.size(), QImage::Format_ARGB32);
|
||||
|
|
|
|||
|
|
@ -297,37 +297,29 @@ vtkImageData* InteractionManager::selectedSliceImage() const {
|
|||
}
|
||||
|
||||
vtkSmartPointer<vtkImageData> InteractionManager::selectedSliceColorImage() const {
|
||||
vtkImageData* scalar = selectedSliceImage();
|
||||
if (scalar == nullptr) return nullptr;
|
||||
if (selected_ < 0 || selected_ >= static_cast<int>(slices_.size())) return nullptr;
|
||||
// 与屏幕切片**同源**的着色输出(widget 自己的 ColorMap 输出, 逐像素一致, RGBA 外区透明)。
|
||||
// 原先另建 LUT 上色, 与屏幕配色可能不一致(用户实测异常截图与切面差异大) → 改取 widget 着色结果。
|
||||
auto colored = slices_[static_cast<std::size_t>(selected_)]->coloredResliceImage();
|
||||
if (colored == nullptr) return nullptr;
|
||||
|
||||
// 高清导出:切片重采样像素维度受体素网格分辨率限制(常仅几十px)→ 先上采样到目标分辨率
|
||||
// (最长边 kExportLongSide,保持长宽比、插值),再上色,得到清晰大图。
|
||||
// 高清化:切片重采样像素维度受体素分辨率限制(常仅几十px) → 上采样到目标分辨率(双线性, 与屏幕
|
||||
// TextureInterpolateOn 同口径), 得清晰大图。对 RGBA 直接插值(色已定, 不再过 LUT)。
|
||||
constexpr int kExportLongSide = 2048;
|
||||
int dims[3];
|
||||
scalar->GetDimensions(dims);
|
||||
colored->GetDimensions(dims);
|
||||
const int nx = dims[0], ny = dims[1];
|
||||
const int longest = std::max(nx, ny);
|
||||
double f = (longest > 0) ? static_cast<double>(kExportLongSide) / longest : 1.0;
|
||||
if (f < 1.0) f = 1.0; // 不缩小(已够大则原样)
|
||||
if (f < 1.0) f = 1.0; // 不缩小
|
||||
vtkNew<vtkImageResize> resize;
|
||||
resize->SetInputData(scalar);
|
||||
resize->SetInputData(colored);
|
||||
resize->SetResizeMethodToOutputDimensions();
|
||||
resize->SetOutputDimensions(std::max(1, static_cast<int>(nx * f)),
|
||||
std::max(1, static_cast<int>(ny * f)), 1);
|
||||
resize->Update();
|
||||
|
||||
// 用与切片显示同一色阶 LUT 上色:取选中切片所属体的色阶(多体并发各体色阶不同)。
|
||||
const VolumeImg* v = (selected_ >= 0 && selected_ < static_cast<int>(slices_.size()))
|
||||
? volumeOf(slices_[static_cast<std::size_t>(selected_)]->volumeDsId())
|
||||
: nullptr;
|
||||
auto lut = v ? buildLut(v->cs, v->vmin, v->vmax) : buildLut(geopro::core::ColorScale{}, 0.0, 1.0);
|
||||
vtkNew<vtkImageMapToColors> map;
|
||||
map->SetInputConnection(resize->GetOutputPort());
|
||||
map->SetLookupTable(lut);
|
||||
map->SetOutputFormatToRGB();
|
||||
map->Update();
|
||||
auto out = vtkSmartPointer<vtkImageData>::New();
|
||||
out->DeepCopy(map->GetOutput()); // 深拷贝脱离 filter 生命周期
|
||||
out->DeepCopy(resize->GetOutput()); // 脱离 filter 生命周期
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <vtkCallbackCommand.h>
|
||||
#include <vtkCommand.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkImageMapToColors.h>
|
||||
#include <vtkImagePlaneWidget.h>
|
||||
#include <vtkLookupTable.h>
|
||||
#include <vtkProperty.h>
|
||||
|
|
@ -170,6 +171,16 @@ vtkImageData* SliceTool::reslicedOutput() const {
|
|||
return widget_ ? widget_->GetResliceOutput() : nullptr;
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkImageData> SliceTool::coloredResliceImage() const {
|
||||
if (!widget_) return nullptr;
|
||||
vtkImageMapToColors* cm = widget_->GetColorMap(); // widget 内部把 reslice 经 LUT 上色 → 纹理
|
||||
if (cm == nullptr) return nullptr;
|
||||
cm->Update();
|
||||
auto out = vtkSmartPointer<vtkImageData>::New();
|
||||
out->DeepCopy(cm->GetOutput()); // 即屏幕切片所贴像素(RGBA, 外区 alpha=0)
|
||||
return out;
|
||||
}
|
||||
|
||||
double SliceTool::distanceToPlane(const Vec3& p) const {
|
||||
const Vec3 c = center();
|
||||
const Vec3 n = normal();
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@ public:
|
|||
|
||||
// 当前切面重采样得到的 2D 标量影像(导出 dat 用);widget 已释放则 nullptr。
|
||||
vtkImageData* reslicedOutput() const;
|
||||
// 与屏幕切片纹理同源的着色输出(widget 自己的 ColorMap 输出, RGBA, 逐像素一致, 外区透明)。
|
||||
// 异常截图/导出用它而非另建 LUT,避免与屏幕配色不一致(用户实测差异大)。
|
||||
vtkSmartPointer<vtkImageData> coloredResliceImage() const;
|
||||
|
||||
// 关闭:Off() 并解除 interactor 绑定(幂等)。
|
||||
void close();
|
||||
|
|
|
|||
Loading…
Reference in New Issue