1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkImageGaussianSmooth.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkImageGaussianSmooth.h"
16 
17 #include "vtkImageData.h"
18 #include "vtkInformation.h"
19 #include "vtkInformationVector.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkStreamingDemandDrivenPipeline.h"
22 
23 #include <cmath>
24 
25 vtkStandardNewMacro(vtkImageGaussianSmooth);
26 
27 //------------------------------------------------------------------------------
vtkImageGaussianSmooth()28 vtkImageGaussianSmooth::vtkImageGaussianSmooth()
29 {
30   this->Dimensionality = 3; // note: this overrides Standard deviation.
31   this->StandardDeviations[0] = 2.0;
32   this->StandardDeviations[1] = 2.0;
33   this->StandardDeviations[2] = 2.0;
34   this->RadiusFactors[0] = 1.5;
35   this->RadiusFactors[1] = 1.5;
36   this->RadiusFactors[2] = 1.5;
37 }
38 
39 //------------------------------------------------------------------------------
40 vtkImageGaussianSmooth::~vtkImageGaussianSmooth() = default;
41 
42 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)43 void vtkImageGaussianSmooth::PrintSelf(ostream& os, vtkIndent indent)
44 {
45   this->Superclass::PrintSelf(os, indent);
46 
47   // int idx;
48 
49   // os << indent << "BoundaryRescale: " << this->BoundaryRescale << "\n";
50 
51   os << indent << "Dimensionality: " << this->Dimensionality << "\n";
52 
53   os << indent << "RadiusFactors: ( " << this->RadiusFactors[0] << ", " << this->RadiusFactors[1]
54      << ", " << this->RadiusFactors[2] << " )\n";
55 
56   os << indent << "StandardDeviations: ( " << this->StandardDeviations[0] << ", "
57      << this->StandardDeviations[1] << ", " << this->StandardDeviations[2] << " )\n";
58 }
59 
60 //------------------------------------------------------------------------------
ComputeKernel(double * kernel,int min,int max,double std)61 void vtkImageGaussianSmooth::ComputeKernel(double* kernel, int min, int max, double std)
62 {
63   int x;
64   double sum;
65 
66   // handle special case
67   if (std == 0.0)
68   {
69     kernel[0] = 1.0;
70     return;
71   }
72 
73   // fill in kernel
74   sum = 0.0;
75   for (x = min; x <= max; ++x)
76   {
77     sum += kernel[x - min] = exp(-(static_cast<double>(x * x)) / (std * std * 2.0));
78   }
79 
80   // normalize
81   for (x = min; x <= max; ++x)
82   {
83     kernel[x - min] /= sum;
84   }
85 }
86 
87 //------------------------------------------------------------------------------
RequestUpdateExtent(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)88 int vtkImageGaussianSmooth::RequestUpdateExtent(vtkInformation* vtkNotUsed(request),
89   vtkInformationVector** inputVector, vtkInformationVector* outputVector)
90 {
91   // get the info objects
92   vtkInformation* outInfo = outputVector->GetInformationObject(0);
93   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
94 
95   int wholeExtent[6], inExt[6];
96 
97   outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt);
98 
99   // Expand filtered axes
100   inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent);
101 
102   this->InternalRequestUpdateExtent(inExt, wholeExtent);
103 
104   inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt, 6);
105 
106   return 1;
107 }
108 
109 //------------------------------------------------------------------------------
InternalRequestUpdateExtent(int * inExt,int * wholeExtent)110 void vtkImageGaussianSmooth::InternalRequestUpdateExtent(int* inExt, int* wholeExtent)
111 {
112   int idx, radius;
113 
114   // Expand filtered axes
115   for (idx = 0; idx < this->Dimensionality; ++idx)
116   {
117     radius = static_cast<int>(this->StandardDeviations[idx] * this->RadiusFactors[idx]);
118     inExt[idx * 2] -= radius;
119     if (inExt[idx * 2] < wholeExtent[idx * 2])
120     {
121       inExt[idx * 2] = wholeExtent[idx * 2];
122     }
123 
124     inExt[idx * 2 + 1] += radius;
125     if (inExt[idx * 2 + 1] > wholeExtent[idx * 2 + 1])
126     {
127       inExt[idx * 2 + 1] = wholeExtent[idx * 2 + 1];
128     }
129   }
130 }
131 
132 //------------------------------------------------------------------------------
133 // For a given position along the convolution axis, this method loops over
134 // all other axes, and performs the convolution. Boundary conditions handled
135 // previously.
136 template <class T>
vtkImageGaussianSmoothExecute(vtkImageGaussianSmooth * self,int axis,double * kernel,int kernelSize,vtkImageData * inData,T * inPtrC,vtkImageData * outData,int outExt[6],T * outPtrC,int * pcycle,int target,int * pcount,int total)137 void vtkImageGaussianSmoothExecute(vtkImageGaussianSmooth* self, int axis, double* kernel,
138   int kernelSize, vtkImageData* inData, T* inPtrC, vtkImageData* outData, int outExt[6], T* outPtrC,
139   int* pcycle, int target, int* pcount, int total)
140 {
141   int maxC, max0 = 0, max1 = 0;
142   int idxC, idx0, idx1, idxK;
143   vtkIdType inIncs[3], outIncs[3];
144   vtkIdType inInc0 = 0, inInc1 = 0, inIncK, outInc0 = 0, outInc1 = 0;
145   T *outPtr1, *outPtr0;
146   T *inPtr1, *inPtr0, *inPtrK;
147   double *ptrK, sum;
148 
149   // I am counting on the fact that tight loops (component on outside)
150   // is more important than cache misses from shuffled access.
151 
152   // Do the correct shuffling of the axes (increments, extents)
153   inData->GetIncrements(inIncs);
154   outData->GetIncrements(outIncs);
155   inIncK = inIncs[axis];
156   maxC = outData->GetNumberOfScalarComponents();
157   switch (axis)
158   {
159     case 0:
160       inInc0 = inIncs[1];
161       inInc1 = inIncs[2];
162       outInc0 = outIncs[1];
163       outInc1 = outIncs[2];
164       max0 = outExt[3] - outExt[2] + 1;
165       max1 = outExt[5] - outExt[4] + 1;
166       break;
167     case 1:
168       inInc0 = inIncs[0];
169       inInc1 = inIncs[2];
170       outInc0 = outIncs[0];
171       outInc1 = outIncs[2];
172       max0 = outExt[1] - outExt[0] + 1;
173       max1 = outExt[5] - outExt[4] + 1;
174       break;
175     case 2:
176       inInc0 = inIncs[0];
177       inInc1 = inIncs[1];
178       outInc0 = outIncs[0];
179       outInc1 = outIncs[1];
180       max0 = outExt[1] - outExt[0] + 1;
181       max1 = outExt[3] - outExt[2] + 1;
182       break;
183   }
184 
185   for (idxC = 0; idxC < maxC; ++idxC)
186   {
187     inPtr1 = inPtrC;
188     outPtr1 = outPtrC;
189     for (idx1 = 0; !self->AbortExecute && idx1 < max1; ++idx1)
190     {
191       inPtr0 = inPtr1;
192       outPtr0 = outPtr1;
193       for (idx0 = 0; idx0 < max0; ++idx0)
194       {
195         inPtrK = inPtr0;
196         ptrK = kernel;
197         sum = 0.0;
198         // too bad this short loop has to be the inner most loop
199         for (idxK = 0; idxK < kernelSize; ++idxK)
200         {
201           sum += *ptrK * static_cast<double>(*inPtrK);
202           ++ptrK;
203           inPtrK += inIncK;
204         }
205         *outPtr0 = static_cast<T>(sum);
206         inPtr0 += inInc0;
207         outPtr0 += outInc0;
208       }
209       inPtr1 += inInc1;
210       outPtr1 += outInc1;
211       // we finished a row ... do we update ???
212       if (total)
213       { // yes this is the main thread
214         *pcycle += max0;
215         if (*pcycle > target)
216         { // yes
217           *pcycle -= target;
218           *pcount += target;
219           self->UpdateProgress(static_cast<double>(*pcount) / static_cast<double>(total));
220           // fprintf(stderr, "count: %d, total: %d, progress: %f\n",
221           //*pcount, total, (double)(*pcount) / (double)total);
222         }
223       }
224     }
225 
226     ++inPtrC;
227     ++outPtrC;
228   }
229 }
230 
231 //------------------------------------------------------------------------------
232 template <class T>
vtkImageGaussianSmoothGetTypeSize(T *)233 size_t vtkImageGaussianSmoothGetTypeSize(T*)
234 {
235   return sizeof(T);
236 }
237 
238 //------------------------------------------------------------------------------
239 // This method convolves over one axis. It loops over the convolved axis,
240 // and handles boundary conditions.
ExecuteAxis(int axis,vtkImageData * inData,int inExt[6],vtkImageData * outData,int outExt[6],int * pcycle,int target,int * pcount,int total,vtkInformation * inInfo)241 void vtkImageGaussianSmooth::ExecuteAxis(int axis, vtkImageData* inData, int inExt[6],
242   vtkImageData* outData, int outExt[6], int* pcycle, int target, int* pcount, int total,
243   vtkInformation* inInfo)
244 {
245   int idxA, max;
246   int wholeExtent[6], wholeMax, wholeMin;
247   double* kernel;
248   // previousClip and currentClip rembers that the previous was not clipped
249   // keeps from recomputing kernels for center pixels.
250   int kernelSize = 0;
251   int kernelLeftClip, kernelRightClip;
252   int previousClipped, currentClipped;
253   int radius, size;
254   void* inPtr;
255   void* outPtr;
256   int coords[3];
257   vtkIdType outIncs[3], outIncA;
258 
259   // Get the correct starting pointer of the output
260   outPtr = outData->GetScalarPointerForExtent(outExt);
261   outData->GetIncrements(outIncs);
262   outIncA = outIncs[axis];
263 
264   // trick to account for the scalar type of the output(used to be only float)
265   switch (outData->GetScalarType())
266   {
267     vtkTemplateMacro(outIncA *=
268       static_cast<vtkIdType>(vtkImageGaussianSmoothGetTypeSize(static_cast<VTK_TT*>(nullptr))));
269     default:
270       vtkErrorMacro("Unknown scalar type");
271       return;
272   }
273 
274   // Determine default starting position of input
275   coords[0] = inExt[0];
276   coords[1] = inExt[2];
277   coords[2] = inExt[4];
278 
279   // get whole extent for boundary checking ...
280   inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent);
281   wholeMin = wholeExtent[axis * 2];
282   wholeMax = wholeExtent[axis * 2 + 1];
283 
284   // allocate memory for the kernel
285   radius = static_cast<int>(this->StandardDeviations[axis] * this->RadiusFactors[axis]);
286   size = 2 * radius + 1;
287   kernel = new double[size];
288 
289   // loop over the convolution axis
290   previousClipped = currentClipped = 1;
291   max = outExt[axis * 2 + 1];
292   for (idxA = outExt[axis * 2]; idxA <= max; ++idxA)
293   {
294     // left boundary condition
295     coords[axis] = idxA - radius;
296     kernelLeftClip = wholeMin - coords[axis];
297     if (kernelLeftClip > 0)
298     { // front of kernel is cut off ("kernelStart" samples)
299       coords[axis] += kernelLeftClip;
300     }
301     else
302     {
303       kernelLeftClip = 0;
304     }
305     // Right boundary condition
306     kernelRightClip = (idxA + radius) - wholeMax;
307     if (kernelRightClip < 0)
308     {
309       kernelRightClip = 0;
310     }
311 
312     // We can only use previous kernel if it is not clipped and new
313     // kernel is also not clipped.
314     currentClipped = kernelLeftClip + kernelRightClip;
315     if (currentClipped || previousClipped)
316     {
317       this->ComputeKernel(kernel, -radius + kernelLeftClip, radius - kernelRightClip,
318         static_cast<double>(this->StandardDeviations[axis]));
319       kernelSize = size - kernelLeftClip - kernelRightClip;
320     }
321     previousClipped = currentClipped;
322 
323     /* now do the convolution on the rest of the axes */
324     inPtr = inData->GetScalarPointer(coords);
325     switch (inData->GetScalarType())
326     {
327       vtkTemplateMacro(vtkImageGaussianSmoothExecute(this, axis, kernel, kernelSize, inData,
328         static_cast<VTK_TT*>(inPtr), outData, outExt, static_cast<VTK_TT*>(outPtr), pcycle, target,
329         pcount, total));
330       default:
331         vtkErrorMacro("Unknown scalar type");
332         return;
333     }
334     outPtr = static_cast<void*>(static_cast<unsigned char*>(outPtr) + outIncA);
335   }
336 
337   // get rid of temporary kernel
338   delete[] kernel;
339 }
340 
341 //------------------------------------------------------------------------------
342 // This method decomposes the gaussian and smooths along each axis.
ThreadedRequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector,vtkImageData *** inData,vtkImageData ** outData,int outExt[6],int id)343 void vtkImageGaussianSmooth::ThreadedRequestData(vtkInformation* vtkNotUsed(request),
344   vtkInformationVector** inputVector, vtkInformationVector* outputVector, vtkImageData*** inData,
345   vtkImageData** outData, int outExt[6], int id)
346 {
347   int inExt[6];
348   int target, count, total, cycle;
349 
350   // for feed back, determine line target to get 50 progress update
351   // update is called every target lines. Progress is computed from
352   // the number of pixels processed so far.
353   count = 0;
354   target = 0;
355   total = 0;
356   cycle = 0;
357   if (id == 0)
358   {
359     // determine the number of pixels.
360     total = this->Dimensionality * (outExt[1] - outExt[0] + 1) * (outExt[3] - outExt[2] + 1) *
361       (outExt[5] - outExt[4] + 1) * inData[0][0]->GetNumberOfScalarComponents();
362     // pixels per update (50 updates)
363     target = total / 50;
364   }
365 
366   // this filter expects that input is the same type as output.
367   if (inData[0][0]->GetScalarType() != outData[0]->GetScalarType())
368   {
369     vtkErrorMacro("Execute: input ScalarType, " << inData[0][0]->GetScalarType()
370                                                 << ", must match out ScalarType "
371                                                 << outData[0]->GetScalarType());
372     return;
373   }
374 
375   // Decompose
376   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
377   vtkInformation* outInfo = outputVector->GetInformationObject(0);
378   int wholeExt[6];
379   inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExt);
380   outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt);
381   this->InternalRequestUpdateExtent(inExt, wholeExt);
382 
383   switch (this->Dimensionality)
384   {
385     case 1:
386       this->ExecuteAxis(
387         0, inData[0][0], inExt, outData[0], outExt, &cycle, target, &count, total, inInfo);
388       break;
389     case 2:
390       int tempExt[6];
391       vtkImageData* tempData;
392       // compute intermediate extent
393       tempExt[0] = inExt[0];
394       tempExt[1] = inExt[1];
395       tempExt[2] = outExt[2];
396       tempExt[3] = outExt[3];
397       tempExt[4] = inExt[4];
398       tempExt[5] = inExt[5];
399       // create a temp data for intermediate results
400       tempData = vtkImageData::New();
401       tempData->SetExtent(tempExt);
402       tempData->AllocateScalars(
403         inData[0][0]->GetScalarType(), inData[0][0]->GetNumberOfScalarComponents());
404       this->ExecuteAxis(
405         1, inData[0][0], inExt, tempData, tempExt, &cycle, target, &count, total, inInfo);
406       this->ExecuteAxis(
407         0, tempData, tempExt, outData[0], outExt, &cycle, target, &count, total, inInfo);
408       // release temporary data
409       tempData->Delete();
410       break;
411     case 3:
412       // we do z first because it is most likely smallest
413       int temp0Ext[6], temp1Ext[6];
414       vtkImageData *temp0Data, *temp1Data;
415       // compute intermediate extents
416       temp0Ext[0] = inExt[0];
417       temp0Ext[1] = inExt[1];
418       temp0Ext[2] = inExt[2];
419       temp0Ext[3] = inExt[3];
420       temp0Ext[4] = outExt[4];
421       temp0Ext[5] = outExt[5];
422 
423       temp1Ext[0] = inExt[0];
424       temp1Ext[1] = inExt[1];
425       temp1Ext[2] = outExt[2];
426       temp1Ext[3] = outExt[3];
427       temp1Ext[4] = outExt[4];
428       temp1Ext[5] = outExt[5];
429 
430       // create a temp data for intermediate results
431       temp0Data = vtkImageData::New();
432       temp0Data->SetExtent(temp0Ext);
433       temp0Data->AllocateScalars(
434         inData[0][0]->GetScalarType(), inData[0][0]->GetNumberOfScalarComponents());
435 
436       temp1Data = vtkImageData::New();
437       temp1Data->SetExtent(temp1Ext);
438       temp1Data->AllocateScalars(
439         inData[0][0]->GetScalarType(), inData[0][0]->GetNumberOfScalarComponents());
440       this->ExecuteAxis(
441         2, inData[0][0], inExt, temp0Data, temp0Ext, &cycle, target, &count, total, inInfo);
442       this->ExecuteAxis(
443         1, temp0Data, temp0Ext, temp1Data, temp1Ext, &cycle, target, &count, total, inInfo);
444       temp0Data->Delete();
445       this->ExecuteAxis(
446         0, temp1Data, temp1Ext, outData[0], outExt, &cycle, target, &count, total, inInfo);
447       temp1Data->Delete();
448       break;
449   }
450 }
451