Fraxinus  2023.01.05-dev+develop.0da12
An IGT application
cxImageTFData.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 
13 /*
14  * sscImageTFData.cpp
15  *
16  * Created on: Mar 15, 2011
17  * Author: christiana
18  */
19 
20 #include "cxImageTFData.h"
21 #include <iostream>
22 #include <QDomDocument>
23 #include <QStringList>
24 #include <vtkColorTransferFunction.h>
25 #include <vtkPiecewiseFunction.h>
26 #include <vtkLookupTable.h>
27 #include <vtkImageData.h>
28 
29 #include "cxVector3D.h"
30 #include "cxImageTF3D.h"
31 
32 #include "cxTypeConversions.h"
33 #include "cxLogger.h"
34 
35 namespace cx
36 {
37 
39 {
40 }
41 
43 {
44 }
45 
47 {
48  mOpacityMap = source->mOpacityMap;
49  mColorMap = source->mColorMap;
50 }
51 
52 void ImageTFData::addXml(QDomNode dataNode)
53 {
54  QDomDocument doc = dataNode.ownerDocument();
55 
56  QDomElement alphaNode = doc.createElement("alpha");
57  // Use QStringList to put all points in the same string instead of storing
58  // the points as separate nodes.
59  QStringList pointStringList;
60  // Add alpha points
61  for (IntIntMap::iterator opPoint = mOpacityMap.begin(); opPoint != mOpacityMap.end(); ++opPoint)
62  pointStringList.append(QString("%1=%2").arg(opPoint->first). arg(opPoint->second));
63  alphaNode.appendChild(doc.createTextNode(pointStringList.join(" ")));
64 
65  pointStringList.clear();
66  QDomElement colorNode = doc.createElement("color");
67  // Add color points
68  for (ColorMap::iterator colorPoint = mColorMap.begin(); colorPoint != mColorMap.end(); ++colorPoint)
69  pointStringList.append(QString("%1=%2/%3/%4").arg(colorPoint->first). arg(colorPoint->second.red()). arg(
70  colorPoint->second.green()). arg(colorPoint->second.blue()));
71  colorNode.appendChild(doc.createTextNode(pointStringList.join(" ")));
72 
73  dataNode.appendChild(alphaNode);
74  dataNode.appendChild(colorNode);
75 
76  QDomElement elem = dataNode.toElement();
77 }
78 
79 void ImageTFData::parseXml(QDomNode dataNode)
80 {
81  if (dataNode.isNull())
82  {
83  CX_LOG_WARNING() << "ImageTFData::parseXml empty data node";
84  return;
85  }
86 
87  QDomNode alphaNode = dataNode.namedItem("alpha");
88  // Read alpha node if it exists
89  if (!alphaNode.isNull() && !alphaNode.toElement().text().isEmpty())
90  {
91  QString alphaString = alphaNode.toElement().text();
92  mOpacityMap.clear();
93  QStringList alphaStringList = alphaString.split(" ", QString::SkipEmptyParts);
94  for (int i = 0; i < alphaStringList.size(); i++)
95  {
96  QStringList pointStringList = alphaStringList[i].split("=");
97  if (pointStringList.size() < 2)
98  continue;
99  addAlphaPoint(pointStringList[0].toInt(), pointStringList[1].toInt());
100  }
101  }
102  else
103  {
104  CX_LOG_WARNING() << "ImageTF3D::parseXml() found no alpha transferfunction";
105  }
106 
107  QDomNode colorNode = dataNode.namedItem("color");
108  // Read color node if it exists
109  if (!colorNode.isNull() && !colorNode.toElement().text().isEmpty())
110  {
111  mColorMap.clear();
112  QStringList colorStringList = colorNode.toElement().text().split(" ", QString::SkipEmptyParts);
113  for (int i = 0; i < colorStringList.size(); i++)
114  {
115  QStringList pointStringList = colorStringList[i].split("=");
116  QStringList valueStringList = pointStringList[1].split("/");
117  addColorPoint(pointStringList[0].toInt(), QColor(valueStringList[0].toInt(), valueStringList[1].toInt(),
118  valueStringList[2].toInt()));
119  }
120  }
121  else
122  {
123  CX_LOG_WARNING() << "ImageTF3D::parseXml() found no color transferfunction";
124  }
125 
126  this->internalsHaveChanged();
127 }
128 
135 void ImageTFData::unsignedCT(bool onLoad)
136 {
137 // //Signed after all. Don't do anyting
138 // if (this->getScalarMin() < 0)
139 // return;
140 
141  int modify = -1024;
142  if(onLoad)
143  modify = 1024;
144 
145 // std::cout << "unsignedCT shift " << modify << std::endl;
146  this->shift(modify);
147 }
148 
149 void ImageTFData::shift(int val)
150 {
151  this->shiftOpacity(val);
152  this->shiftColor(val, 0, 1);
153 
154  this->internalsHaveChanged();
155 }
156 
157 void ImageTFData::shiftColor(int shift, double center, double scale)
158 {
159  ColorMap newColorMap;
160  for (ColorMap::iterator it = mColorMap.begin(); it != mColorMap.end(); ++it)
161  {
162  double newVal = (it->first-center)*scale+center + shift;
163  int roundedVal = floor(newVal + 0.5);
164  newColorMap[roundedVal] = it->second;
165  }
166  mColorMap = newColorMap;
167 }
168 
170 {
171  IntIntMap newOpacipyMap;
172  for (IntIntMap::iterator it = mOpacityMap.begin(); it != mOpacityMap.end(); ++it)
173  {
174  newOpacipyMap[it->first + shift] = it->second;
175  }
176  mOpacityMap = newOpacipyMap;
177 }
178 
182 void ImageTFData::setLLR(double val)
183 {
184  double old = this->getLLR();
185  if (similar(old, val))
186  return;
187 
188  this->shiftOpacity(val-old);
189  this->internalsHaveChanged();
190 }
191 
192 double ImageTFData::getLLR() const
193 {
194  if (mOpacityMap.empty())
195  return 0;
196 
197  for (IntIntMap::const_iterator it = mOpacityMap.begin(); it != mOpacityMap.end(); ++it)
198  {
199  if (!similar(it->second, 0.0))
200  return it->first;
201  }
202  return mOpacityMap.begin()->first;
203 }
204 
205 void ImageTFData::setAlpha(double val)
206 {
207  double old = this->getAlpha();
208  if (similar(old, val))
209  return;
210 
211  if (similar(old, 0.0))
212  {
213  // degenerate case: we have lost all info, simpl add input val to all but the first entry
214  for (IntIntMap::iterator it = mOpacityMap.begin(); it != mOpacityMap.end(); ++it)
215  {
216  if (it==mOpacityMap.begin() && mOpacityMap.size()>1)
217  continue; // heuristic: assume first entry should stay at zero
218  it->second += val*255;
219  }
220  }
221  else
222  {
223  double scale = val/old;
224  for (IntIntMap::iterator it = mOpacityMap.begin(); it != mOpacityMap.end(); ++it)
225  {
226  it->second *= scale;
227  }
228  }
229 
230  this->internalsHaveChanged();
231 }
232 
233 double ImageTFData::getAlpha() const
234 {
235  double amax = 0;
236  for (IntIntMap::const_iterator it = mOpacityMap.begin(); it != mOpacityMap.end(); ++it)
237  {
238  amax = std::max<double>(it->second, amax);
239  }
240  return amax/255;
241 }
242 
246 void ImageTFData::setWindow(double val)
247 {
248  double old = this->getWindow();
249  val = std::max(1.0, val);
250 
251  if (similar(old, val))
252  return;
253 
254  double scale = val/old;
255  this->shiftColor(0, this->getLevel(), scale);
256 
257  this->internalsHaveChanged();
258 }
259 
261 {
262  if (mColorMap.empty())
263  return 0;
264  return mColorMap.rbegin()->first - mColorMap.begin()->first;
265 // return mWindow;
266 }
267 
271 void ImageTFData::setLevel(double val)
272 {
273  double old = this->getLevel();
274  if (similar(old, val))
275  return;
276  double shift = val-old;
277 
278  this->shiftColor(shift, 0.0, 1.0);
279 
280 // mLevel = val;
281  this->internalsHaveChanged();
282 }
283 
284 double ImageTFData::getLevel() const
285 {
286  if (mColorMap.empty())
287  return 0;
288  int a = mColorMap.begin()->first;
289  int b = mColorMap.rbegin()->first;
290  return a + (b-a)/2;
291 // return mLevel;
292 }
293 
295 {
296  return mOpacityMap;
297 }
299 {
300  return mColorMap;
301 }
302 void ImageTFData::addAlphaPoint(int alphaPosition, int alphaValue)
303 {
304  //mOpacityMapPtr->insert(std::pair<int, int>(alphaPosition, alphaValue));
305  mOpacityMap[alphaPosition] = alphaValue;
306  this->internalsHaveChanged();
307 }
308 void ImageTFData::removeAlphaPoint(int alphaPosition)
309 {
310  mOpacityMap.erase(alphaPosition);
311  this->internalsHaveChanged();
312 }
313 void ImageTFData::moveAlphaPoint(int oldpos, int newpos, int alphaValue)
314 {
315  mOpacityMap.erase(oldpos);
316  mOpacityMap[newpos] = alphaValue;
317  this->internalsHaveChanged();
318 }
319 void ImageTFData::addColorPoint(int colorPosition, QColor colorValue)
320 {
321  mColorMap[colorPosition] = colorValue;
322  //mColorMapPtr->insert(std::pair<int, QColor>(colorPosition, colorValue));
323  this->internalsHaveChanged();
324 }
325 void ImageTFData::removeColorPoint(int colorPosition)
326 {
327  mColorMap.erase(colorPosition);
328  this->internalsHaveChanged();
329 }
330 
331 void ImageTFData::moveColorPoint(int oldpos, int newpos, QColor colorValue)
332 {
333  mColorMap.erase(oldpos);
334  mColorMap[newpos] = colorValue;
335  this->internalsHaveChanged();
336 }
337 
339 {
340  mOpacityMap = val;
341  this->internalsHaveChanged();
342 }
343 
345 {
346  mColorMap = val;
347  this->internalsHaveChanged();
348 }
349 
351 {
352  vtkColorTransferFunctionPtr tf = vtkColorTransferFunctionPtr::New();
353  this->fillColorTFFromMap(tf);
354  return tf;
355 }
356 
358 {
359  vtkPiecewiseFunctionPtr tf = vtkPiecewiseFunctionPtr::New();
360  this->fillOpacityTFFromMap(tf);
361  return tf;
362 }
363 
365 {
366  tf->SetColorSpaceToRGB();
367  tf->RemoveAllPoints();
368  for (ColorMap::const_iterator iter = mColorMap.begin(); iter != mColorMap.end(); ++iter)
369  {
370  QColor c = iter->second;
371  tf->AddRGBPoint(iter->first, c.redF(), c.greenF(), c.blueF());
372  }
373 }
374 
376 {
377  tf->RemoveAllPoints();
378  for (IntIntMap::const_iterator iter = mOpacityMap.begin(); iter != mOpacityMap.end(); ++iter)
379  tf->AddPoint(iter->first, iter->second / 255.0);
380 // tf->Update();
381 }
382 
383 }
void setLLR(double val)
range [scalarMin..scalarMax]
IntIntMap mOpacityMap
double getLLR() const
virtual ~ImageTFData()
void unsignedCT(bool onLoad)
void shiftColor(int shift, double center, double scale)
virtual void addXml(QDomNode dataNode)
adds xml information about the transferfunction and its variabels
void fillOpacityTFFromMap(vtkPiecewiseFunctionPtr tf) const
virtual void parseXml(QDomNode dataNode)
Use a XML node to load data.
vtkColorTransferFunctionPtr generateColorTF() const
void shift(int val)
shift the transfter function index values by the input amount. Used for signed/unsigned conversion...
vtkSmartPointer< class vtkPiecewiseFunction > vtkPiecewiseFunctionPtr
void resetColor(ColorMap val)
void setAlpha(double val)
range [0..1]
double getAlpha() const
ColorMap getColorMap()
vtkSmartPointer< class vtkColorTransferFunction > vtkColorTransferFunctionPtr
void removeAlphaPoint(int alphaPosition)
Remove point from the opacity transfer function.
std::map< int, QColor > ColorMap
Definition: cxImage.h:36
void setWindow(double val)
range [1..scalarMax-scalarMin]
void moveColorPoint(int oldpos, int newpos, QColor colorValue)
void removeColorPoint(int colorPosition)
Remove point from the color transfer function.
vtkPiecewiseFunctionPtr generateOpacityTF() const
double getLevel() const
virtual void internalsHaveChanged()
void resetAlpha(IntIntMap val)
std::map< int, int > IntIntMap
Definition: cxImage.h:35
IntIntMap getOpacityMap()
Data class for Transfer Function info, either 2D or 3D.
Definition: cxImageTFData.h:76
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
#define CX_LOG_WARNING
Definition: cxLogger.h:98
void setLevel(double val)
range [scalarMin..scalarMax]
ColorMap mColorMap
void addAlphaPoint(int alphaPosition, int alphaValue)
Add point to the opacity transfer function.
void moveAlphaPoint(int oldpos, int newpos, int alphaValue)
double getWindow() const
void shiftOpacity(int shift)
void addColorPoint(int colorPosition, QColor colorValue)
Add point to the color transfer function.
void deepCopy(ImageTFData *source)
void fillColorTFFromMap(vtkColorTransferFunctionPtr tf) const
Namespace for all CustusX production code.