vtk重连管线 分段颜色映射(RGBA图像生成)
解决的问题:1、在代码实现过程中往往需要重新连接管线,因为管线中的某些设置已经过时。2、对于整个图像使用一幅颜色映射,但是只显示其中某几段的颜色,方案设计。
直接上代码
// 构造函数中初始化管线
void tabHistogram::initVtkConnection()
{
for(int i = 0; i < 3; i++) {
image_slice[i] = vtkSmartPointer<vtkImageSlice>::New();
image_sliceMapper[i] = vtkSmartPointer<vtkImageSliceMapper>::New();
image_slice[i]->SetMapper(image_sliceMapper[i]);
auto mainReslice = Singleton::getInstance()->curItem->m_ImageReslices[i];
// 生成 RGBA 管线
// std::vector<std::pair<double, double>> intervals = {};
// auto rgbaPort = CreateRGBAIntervalSlicePipeline(mainReslice->GetOutputPort(), intervals, cavasLookup);
// // 连接到 mapper
// image_sliceMapper[i]->SetInputConnection(rgbaPort->GetOutputPort());
// // slice property
// vtkImageProperty* prop = image_slice[i]->GetProperty();
// prop->SetOpacity(1.0); // RGBA alpha 已经处理
// prop->UseLookupTableScalarRangeOff(); // RGBA 已经 bake,不用 LUT
// Singleton::getInstance()->mainwindow->getRenderer(i)->AddViewProp(image_slice[i]);
}
}
// 在管线中条件变化,比如分段区间变化时,重新构建管线
void tabHistogram::rebuildVtkConnection()
{
for(int i = 0; i < 3; i++) {
auto mainReslice = Singleton::getInstance()->curItem->m_ImageReslices[i];
// 生成 RGBA 管线
auto rgbaPort = CreateRGBAIntervalSlicePipeline(mainReslice->GetOutputPort(), m_intervals, cavasLookup);
if(rgbaPort == nullptr) {
image_sliceMapper[i]->RemoveAllInputs();
Singleton::getInstance()->mainwindow->getRenderer(i)->RemoveViewProp(image_slice[i]);
Singleton::getInstance()->mainwindow->updateRender(i);
continue;
}
// 连接到 mapper
image_sliceMapper[i]->SetInputConnection(rgbaPort->GetOutputPort());
// slice property
vtkImageProperty* prop = image_slice[i]->GetProperty();
prop->SetOpacity(1.0); // RGBA alpha 已经处理
prop->UseLookupTableScalarRangeOff(); // RGBA 已经 bake,不用 LUT
Singleton::getInstance()->mainwindow->getRenderer(i)->AddViewProp(image_slice[i]);
Singleton::getInstance()->mainwindow->updateRender(i);
}
}
// 核心代码:生成RGBA图像,vtkImageAppendComponents就是做这个事情的,生成四通道数据,给vtkImageActor或者vtkImageSlice时会自动对四通道图像做RGBA显示。(Maptocolors RGB)(A)
// 由此引发的联想,以vtkImageActor为例,它是单通道图像所以没有办法颜色渲染,所以通过vtkMaptocolors生成三通道,就是我们平时做的颜色渲染。四通道则更进一步
// 输入:reslice 输出 image,区间列表,LUT
static vtkSmartPointer<vtkImageAppendComponents> CreateRGBAIntervalSlicePipeline(vtkAlgorithmOutput* inputPort,
const std::vector<std::pair<double, double>>& intervals, vtkLookupTable* lut)
{
if (!inputPort || intervals.empty())
return nullptr;
// ===============================
// 1. LUT 映射 RGB
// ===============================
auto mapToColors = vtkSmartPointer<vtkImageMapToColors>::New();
mapToColors->SetInputConnection(inputPort);
mapToColors->SetLookupTable(lut);
mapToColors->SetOutputFormatToRGB();
mapToColors->PassAlphaToOutputOff();
// ===============================
// 2. 生成 Alpha Mask
// ===============================
std::vector<vtkSmartPointer<vtkImageThreshold>> masks;
for (auto& interval : intervals)
{
auto mask = vtkSmartPointer<vtkImageThreshold>::New();
mask->SetInputConnection(inputPort);
mask->ThresholdBetween(interval.first, interval.second);
mask->SetInValue(255); // 区间内 alpha 255
mask->SetOutValue(0); // 区间外 alpha 0
mask->SetOutputScalarTypeToUnsignedChar();
masks.push_back(mask);
}
// 多个 mask 合并(取最大值)
vtkSmartPointer<vtkImageAlgorithm> alphaMask = masks[0];
for (size_t i = 1; i < masks.size(); ++i)
{
auto math = vtkSmartPointer<vtkImageMathematics>::New();
math->SetOperationToMax();
math->SetInputConnection(0, alphaMask->GetOutputPort());
math->SetInputConnection(1, masks[i]->GetOutputPort());
alphaMask = math;
}
// ===============================
// 3. 合并 RGB + Alpha → RGBA
// ===============================
auto appendRGBA = vtkSmartPointer<vtkImageAppendComponents>::New();
appendRGBA->AddInputConnection(mapToColors->GetOutputPort()); // RGB
appendRGBA->AddInputConnection(alphaMask->GetOutputPort()); // Alpha
return appendRGBA;
}
void tabHistogram::on_Bn_showPeak_clicked()
{
QList<QTableWidgetItem*> items = ui->tableWidget->selectedItems();
if(items.size() > 0) {
int row = items[0]->row();
if(ui->tableWidget->item(row, STU)->text() == tr("未显示")) {
double pos = ui->tableWidget->item(row, POS)->text().toDouble();
double wid = ui->tableWidget->item(row, WID)->text().toDouble();
ui->tableWidget->item(row, STU)->setText(tr("已显示"));
ui->Bn_showPeak->setText(tr("取消显示峰"));
} else {
double pos = ui->tableWidget->item(row, POS)->text().toDouble();
double wid = ui->tableWidget->item(row, WID)->text().toDouble();
ui->tableWidget->item(row, STU)->setText(tr("未显示"));
ui->Bn_showPeak->setText(tr("显示峰"));
}
}
std::vector<Interval> intervals;
for(int i = 0; i < ui->tableWidget->rowCount(); i++) {
if(ui->tableWidget->item(i, STU)->text() == tr("已显示")) {
double pos = ui->tableWidget->item(i, POS)->text().toDouble();
double wid = ui->tableWidget->item(i, WID)->text().toDouble();
intervals.push_back({pos - wid / 2, pos + wid / 2});
}
}
mergeIntervals(intervals);
m_intervals = intervals;
// 重建二维渲染管线
rebuildVtkConnection();
// 建立三维渲染
auto item = Singleton::getInstance()->curItem;
auto m_colorTransferFunction = item->m_colorTransferFunction;
auto m_opacityTransferFunction = item->m_opacityTransferFunction;
auto m_volumeProperty = item->m_volumeProperty;
auto m_volumeMapper = item->m_volumeMapper;
Double2 range;
item->m_data->GetScalarRange(range.data());
double huMin = range[0];
double huMax = range[1];
m_colorTransferFunction->RemoveAllPoints();
int n = cavasLookup->GetNumberOfTableValues();
for (int i = 0; i < n; ++i) {
double rgb[4];
cavasLookup->GetTableValue(i, rgb);
double value = huMin + (huMax - huMin) * i / (n-1);
m_colorTransferFunction->AddRGBPoint(value, rgb[0], rgb[1], rgb[2]);
}
m_opacityTransferFunction->RemoveAllPoints();
// 先把整个范围设为透明
m_opacityTransferFunction->AddPoint(cavasLookup->GetRange()[0], 0.0);
m_opacityTransferFunction->AddPoint(cavasLookup->GetRange()[1], 0.0);
// 遍历 intervals,把对应区间 alpha 设置为 1
for (auto& interval : m_intervals)
{
double start = interval.first;
double end = interval.second;
// 区间前一点保持透明
m_opacityTransferFunction->AddPoint(start - 0.001, 0.0);
// 区间内显示
m_opacityTransferFunction->AddPoint(start, 1.0);
m_opacityTransferFunction->AddPoint(end, 1.0);
// 区间后一点透明
m_opacityTransferFunction->AddPoint(end + 0.001, 0.0);
}
Singleton::getInstance()->mainwindow->updateRender(3);
}