CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxColorVariationFilter.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 #include <random>
13 #include <cmath>
14 
15 #include <vtkCellData.h>
16 #include <vtkUnsignedCharArray.h>
17 #include <vtkPolyData.h>
18 
19 #include "cxColorVariationFilter.h"
20 #include "cxTypeConversions.h"
22 #include "cxData.h"
23 #include "cxDoubleProperty.h"
24 #include "cxStringProperty.h"
25 #include "cxBoolProperty.h"
26 #include "cxVisServices.h"
27 #include "cxMesh.h"
28 #include "vtkForwardDeclarations.h"
29 #include "cxLogger.h"
31 
32 namespace cx
33 {
34 
36  FilterImpl(services)
37 {
38 }
39 
41 {
42  return "Color variation";
43 }
44 
46 {
47  return "color_variation_filter";
48 }
49 
51 {
52  return "_colored";
53 }
54 
56 {
57  return "A filter to create variation of colors to a mesh. \n"
58  "The variation is applied as a normal distributin \n"
59  "with the original color as mean. A filter \n"
60  "is applied for smooth color transitions.";
61 }
62 
63 DoublePropertyPtr ColorVariationFilter::getGlobalVarianceOption(QDomElement root)
64 {
65  DoublePropertyPtr retval = DoubleProperty::initialize("Global variance", "",
66  "Select the global color variance", 10.0, DoubleRange(1.0, 100.0, 0.5), 1.0, root);
67  return retval;
68 }
69 
70 DoublePropertyPtr ColorVariationFilter::getLocalVarianceOption(QDomElement root)
71 {
72  DoublePropertyPtr retval = DoubleProperty::initialize("Local variance", "",
73  "Select the local color variance", 1.0, DoubleRange(0.1, 10.0, 0.1), 1.0, root);
74  return retval;
75 }
76 
77 DoublePropertyPtr ColorVariationFilter::getSmoothingOption(QDomElement root)
78 {
79  DoublePropertyPtr retval = DoubleProperty::initialize("Smoothing", "",
80  "Select the number of smoothing iterations", 3, DoubleRange(0, 10, 1), 0, root);
81  return retval;
82 }
83 
85 {
86  mOptionsAdapters.push_back(this->getGlobalVarianceOption(mOptions));
87  mOptionsAdapters.push_back(this->getLocalVarianceOption(mOptions));
88  mOptionsAdapters.push_back(this->getSmoothingOption(mOptions));
89 }
90 
92 {
94 
95  temp = StringPropertySelectMesh::New(mServices->patient());
96  temp->setValueName("Input");
97  temp->setHelp("Input mesh to be applied color variation.");
98  mInputTypes.push_back(temp);
99 }
100 
102 {
104 
105  temp = StringPropertySelectMesh::New(mServices->patient());
106  temp->setValueName("Output");
107  temp->setHelp("Output mesh");
108  mOutputTypes.push_back(temp);
109 }
110 
112 {
113  MeshPtr inputMesh = boost::dynamic_pointer_cast<StringPropertySelectMesh>(mInputTypes[0])->getMesh();
114  if (!inputMesh)
115  return false;
116 
117  double globaleVatiance = this->getGlobalVarianceOption(mOptions)->getValue();
118  double localeVatiance = this->getLocalVarianceOption(mOptions)->getValue();
119  int smoothingIterations = int(this->getSmoothingOption(mOptions)->getValue());
120 
121  mOutputMesh = this->execute(inputMesh, globaleVatiance, localeVatiance, smoothingIterations);
122 
123  if(mOutputTypes.size() > 0)
124  mOutputTypes[0]->setValue(mOutputMesh->getUid());
125  return true;
126 }
127 
128 MeshPtr ColorVariationFilter::execute(MeshPtr inputMesh, double globaleVariance, double localeVariance, int smoothingIterations)
129 {
130  if (!inputMesh)
131  return MeshPtr();
132 
133  vtkPolyDataPtr polyData = inputMesh->getTransformedPolyDataCopy(inputMesh->get_rMd());
134  mGlobalVariance = globaleVariance;
135  mLocalVariance = localeVariance;
136 
137  this->sortPolyData(polyData);
138  this->colorPolyData(inputMesh);
139  this->smoothColorsInMesh(smoothingIterations);
140  polyData->GetCellData()->SetScalars(mColors);
141 
142  QString uidColoredModel = inputMesh->getUid() + ColorVariationFilter::getNameSuffixColorVariation();
143  QString nameColoredModel = inputMesh->getName() + ColorVariationFilter::getNameSuffixColorVariation();
144 
145  MeshPtr outputMesh = patientService()->createSpecificData<Mesh>(uidColoredModel, nameColoredModel);
146  outputMesh->setVtkPolyData(polyData);
147  outputMesh->setColor(inputMesh->getColor());
148  outputMesh->setUseColorFromPolydataScalars(true);
149  outputMesh->get_rMd_History()->setParentSpace(inputMesh->getParentSpace());
150  patientService()->insertData(outputMesh);
151 
152  return outputMesh;
153 }
154 
156 {
157  return mOutputMesh;
158 }
159 
161 {
162  if (mOutputTypes.front()->getData())
163  mOutputTypes.front()->setValue(mOutputTypes.front()->getData()->getUid());
164 
165  return true;
166 }
167 
168 void ColorVariationFilter::sortPolyData(vtkPolyDataPtr polyData)
169 {
170  mPolyToPointsArray.clear();
171  mPointToPolysArray.clear();
172 
173  vtkIdType numberOfCells = polyData->GetNumberOfCells();
174 
175  mPolyToPointsArray = std::vector<std::vector<vtkIdType>>(numberOfCells);
176 
177  std::vector<std::vector<vtkIdType>> pointToPolysArray(polyData->GetNumberOfPoints());
178  for(vtkIdType i = 0; i < numberOfCells; i++)
179  {
180  vtkIdListPtr points = polyData->GetCell(i)->GetPointIds();
181  vtkIdType numberOfIds = points->GetNumberOfIds();
182  std::vector<vtkIdType> pointsArray(numberOfIds);
183  for(vtkIdType j = 0; j < numberOfIds; j++)
184  {
185  vtkIdType p = points->GetId(j);
186  pointsArray[j]= p;
187  pointToPolysArray[p].resize(pointToPolysArray[p].size()+1, i);
188  }
189  mPolyToPointsArray[i] = pointsArray;
190  }
191  mPointToPolysArray = pointToPolysArray;
192 
193 }
194 
195 vtkUnsignedCharArrayPtr ColorVariationFilter::colorPolyData(MeshPtr mesh)
196 {
197  if(mPolyToPointsArray.empty() || mPointToPolysArray.empty())
198  return mColors;
199 
200  vtkPolyDataPtr polyData = mesh->getTransformedPolyDataCopy(Transform3D::Identity());
201 
202  mAssignedColorValues.clear();
203  mColors = vtkUnsignedCharArrayPtr::New();
204  mColors->SetNumberOfComponents(3);
205  int numberOfPolys = polyData->GetNumberOfCells();
206  mColors->SetNumberOfTuples(numberOfPolys);
207  QColor originalColor = mesh->getColor();
208 
209  mR_mean = originalColor.red();
210  mG_mean = originalColor.green();
211  mB_mean = originalColor.blue();
212 
213  this->generateColorDistribution();
214 
215  mAssignedColorValues.clear();
216  mAssignedColorValues = std::vector<bool>(numberOfPolys, false);
217 
218  for(int i=0; i<numberOfPolys; i++) //Loop needed if multiple independent meshes in model.
219  if(!mAssignedColorValues[i])
220  for(int j=0; j<mPolyToPointsArray[i].size(); j++)
221  this->applyColorToNeighbourPolys(mPolyToPointsArray[i][j], mR_mean, mG_mean, mB_mean);
222 
223  return mColors;
224 }
225 
226 void ColorVariationFilter::applyColorToNeighbourPolys(int startIndex, double R, double G, double B)
227 {
228 
229  std::vector<vtkIdType> polyIndexColoringQueue = this->applyColorAndFindNeighbours(startIndex, R, G, B);
230  std::vector<double> color {R, G, B};
231  std::vector<std::vector<double>> polyColorColoringQueue(polyIndexColoringQueue.size(), color);
232 
233  while(!polyIndexColoringQueue.empty())
234  {
235  std::vector<vtkIdType> neighbourPointsList = mPolyToPointsArray[polyIndexColoringQueue[0]]; //Prosess first index in FIFO queue
236 
237  for(int i=0; i<neighbourPointsList.size(); i++)
238  {
239  std::vector<double> newColor = generateColor(polyColorColoringQueue[0][0], polyColorColoringQueue[0][1], polyColorColoringQueue[0][2]);
240  std::vector<vtkIdType> polyIndexToColor = this->applyColorAndFindNeighbours(neighbourPointsList[i], newColor[0], newColor[1], newColor[2]);
241  polyIndexColoringQueue.insert(polyIndexColoringQueue.end(), polyIndexToColor.begin(), polyIndexToColor.end());
242  polyColorColoringQueue.resize(polyColorColoringQueue.size()+polyIndexToColor.size(), newColor);
243  }
244  polyIndexColoringQueue.erase(polyIndexColoringQueue.begin());
245  polyColorColoringQueue.erase(polyColorColoringQueue.begin());
246  }
247 }
248 
249 std::vector<vtkIdType> ColorVariationFilter::applyColorAndFindNeighbours(int pointIndex, double R, double G, double B)
250 {
251  std::vector<vtkIdType> neighbourPolysList = mPointToPolysArray[pointIndex];
252  std::vector<int> removeIndexList;
253 
254  for(int i=0; i<neighbourPolysList.size(); i++)
255  {
256  if(!mAssignedColorValues[neighbourPolysList[i]]) //Check if tuple is already assigned a color
257  {
258  mColors->InsertTuple3(neighbourPolysList[i], R, G, B);
259  mAssignedColorValues[neighbourPolysList[i]] = true;
260  }
261  else
262  {
263  removeIndexList.push_back(i); //Remove tuple if already assigned
264  }
265  }
266 
267  int N = removeIndexList.size();
268  for(int i=N-1; i>=0; i--) //Removeing neighbour polys which is already assigned a color
269  neighbourPolysList.erase(neighbourPolysList.begin() + removeIndexList[i]);
270 
271  return neighbourPolysList;
272 }
273 
274 void ColorVariationFilter::generateColorDistribution()
275 {
276  mR_dist = std::normal_distribution<> {mR_mean, mGlobalVariance};
277  mG_dist = std::normal_distribution<> {mG_mean, mGlobalVariance};
278  mB_dist = std::normal_distribution<> {mB_mean, mGlobalVariance};
279 }
280 
281 std::vector<double> ColorVariationFilter::generateColor(double R, double G, double B)
282 {
283 
284  std::vector<double> color;
285  color.push_back( std::max(std::min( std::max(std::min(mR_dist(m_gen),R+mLocalVariance),R-mLocalVariance) ,254.999),0.0001) );
286  color.push_back( std::max(std::min( std::max(std::min(mG_dist(m_gen),G+mLocalVariance),G-mLocalVariance) ,254.999),0.0001) );
287  color.push_back( std::max(std::min( std::max(std::min(mB_dist(m_gen),B+mLocalVariance),B-mLocalVariance) ,254.999),0.0001) );
288 
289  return color;
290 }
291 
292 void ColorVariationFilter::smoothColorsInMesh(int iterations)
293 {
294  for(int itr=0; itr<iterations; itr++)
295  {
296  vtkUnsignedCharArrayPtr newColors = mColors;
297  int numberofPolys = newColors->GetNumberOfTuples();
298 
299  Eigen::MatrixXd allColors(numberofPolys,3);
300  for(int i=0; i<numberofPolys; i++)
301  {
302  double colorTuple[3];
303  mColors->GetTuple(i, colorTuple);
304  allColors(i,0) = colorTuple[0];
305  allColors(i,1) = colorTuple[1];
306  allColors(i,2) = colorTuple[2];
307  }
308 
309  for(int i=0; i<numberofPolys; i++)
310  {
311  std::vector<vtkIdType> connectedPoints = mPolyToPointsArray[i];
312  std::vector<int> neighbourPolys;
313  for(int j=0; j<connectedPoints.size(); j++)
314  {
315  std::vector<vtkIdType> newNeighbours = mPointToPolysArray[connectedPoints[j]];
316  neighbourPolys.insert(neighbourPolys.end(), newNeighbours.begin(), newNeighbours.end());
317  }
318  neighbourPolys.erase( unique( neighbourPolys.begin(), neighbourPolys.end() ), neighbourPolys.end() );// remove duplicates
319  std::vector<std::vector<double>> neighbourColors;
320  Eigen::Vector3d sumNeighbourColors = Eigen::Vector3d::Zero();
321  for(int j=0; j<neighbourPolys.size(); j++)
322  {
323  sumNeighbourColors(0) += allColors(neighbourPolys[j],0);
324  sumNeighbourColors(1) += allColors(neighbourPolys[j],1);
325  sumNeighbourColors(2) += allColors(neighbourPolys[j],2);
326  }
327 
328  Eigen::Vector3d color;
329  for(int j=0; j<3; j++)
330  color(j) = ( sumNeighbourColors(j) + allColors(i,j) ) / (neighbourPolys.size() + 1); //Average of current color and all neighbour colors
331 
332  newColors->InsertTuple3(i, color[0], color[1], color[2]);
333  }
334 
335  mColors = newColors;
336  }
337 }
338 
339 } // namespace cx
std::vector< SelectDataStringPropertyBasePtr > mInputTypes
Definition: cxFilterImpl.h:73
A mesh data set.
Definition: cxMesh.h:45
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
virtual QString getHelp() const
ColorVariationFilter(VisServicesPtr services)
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:32
vtkSmartPointer< class vtkIdList > vtkIdListPtr
vtkSmartPointer< class vtkUnsignedCharArray > vtkUnsignedCharArrayPtr
std::vector< PropertyPtr > mOptionsAdapters
Definition: cxFilterImpl.h:75
VisServicesPtr mServices
Definition: cxFilterImpl.h:82
boost::shared_ptr< class SelectDataStringPropertyBase > SelectDataStringPropertyBasePtr
PatientModelServicePtr patientService()
void setVtkPolyData(const vtkPolyDataPtr &polyData)
Definition: cxMesh.cpp:92
QDomElement mOptions
Definition: cxFilterImpl.h:76
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
boost::shared_ptr< class DoubleProperty > DoublePropertyPtr
std::vector< SelectDataStringPropertyBasePtr > mOutputTypes
Definition: cxFilterImpl.h:74
static QString getNameSuffixColorVariation()
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
static StringPropertySelectMeshPtr New(PatientModelServicePtr patientModelService)
boost::shared_ptr< class Mesh > MeshPtr
virtual QString getType() const
virtual QString getName() const
Namespace for all CustusX production code.