21 #ifndef otbFastNLMeansImageFilter_hxx
22 #define otbFastNLMeansImageFilter_hxx
25 #include "itkImageRegionIterator.h"
26 #include "itkImageRegionConstIterator.h"
27 #include "itkNumericTraits.h"
33 template<
class TInputImage,
class TOutputImage>
37 m_HalfSearchSize.Fill(7);
38 m_HalfPatchSize.Fill(2);
40 m_CutoffDistance = 0.1;
42 m_NormalizeDistance = m_CutoffDistance * m_CutoffDistance
43 * (2*m_HalfPatchSize[m_ROW]+1) * (2*m_HalfPatchSize[m_COL]+1);
46 template<
class TInputImage,
class TOutputImage>
47 std::tuple< typename NLMeansFilter<TInputImage, TOutputImage>::InRegionType,
48 int, int, int, int,
bool>
53 auto const& inputSize = inputPtr->GetLargestPossibleRegion().GetSize();
56 auto const& outIndex = outputRegion.GetIndex();
57 auto const& outSize = outputRegion.GetSize();
60 const InSizeType halfMargin = m_HalfSearchSize + m_HalfPatchSize;
62 const InSizeType fullMargin = sizeTwo*halfMargin;
66 InSizeType requestedSize = outSize + fullMargin;
69 bool needMirrorPadding =
false;
70 int mirrorFirstRow = 0;
71 int mirrorFirstCol = 0;
72 int mirrorLastRow = 0;
73 int mirrorLastCol = 0;
76 if (inIndex[m_COL] < 0){
77 needMirrorPadding =
true;
78 mirrorFirstCol = -inIndex[m_COL];
80 requestedSize[m_COL] -= mirrorFirstCol;
82 if (inIndex[m_ROW] < 0){
83 needMirrorPadding =
true;
84 mirrorFirstRow = -inIndex[m_ROW];
86 requestedSize[m_ROW] -= mirrorFirstRow;
88 unsigned int lastCol = inIndex[m_COL] + requestedSize[m_COL];
89 if (lastCol >= inputSize[m_COL]){
90 needMirrorPadding =
true;
91 mirrorLastCol = lastCol - inputSize[m_COL];
92 requestedSize[m_COL] -= mirrorLastCol;
94 unsigned int lastRow = inIndex[m_ROW] + requestedSize[m_ROW];
95 if (lastRow >= inputSize[m_ROW]){
96 needMirrorPadding =
true;
97 mirrorLastRow = lastRow - inputSize[m_ROW];
98 requestedSize[m_ROW] -= mirrorLastRow;
102 return std::make_tuple(inRequestedRegion, mirrorFirstRow, mirrorFirstCol,
103 mirrorLastRow, mirrorLastCol, needMirrorPadding);
106 template <
class TInputImage,
class TOutputImage>
111 Superclass::GenerateInputRequestedRegion();
113 auto const& outputRequestedRegion = this->GetOutput()->GetRequestedRegion();
114 auto regionAndMirror = this->OutputRegionToInputRegion(outputRequestedRegion);
115 InRegionType inRequestedRegion = std::get<0>(regionAndMirror);
118 inputPtr->SetRequestedRegion(inRequestedRegion);
121 template<
class TInputImage,
class TOutputImage>
125 itk::ThreadIdType itkNotUsed(threadId))
128 auto regionAndMirror = OutputRegionToInputRegion(outputRegionForThread);
130 InRegionType inputRegionForThread = std::get<0>(regionAndMirror);
131 int mirrorFirstRow = std::get<1>(regionAndMirror);
132 int mirrorFirstCol = std::get<2>(regionAndMirror);
133 int mirrorLastRow = std::get<3>(regionAndMirror);
134 int mirrorLastCol = std::get<4>(regionAndMirror);
135 bool needMirror = std::get<5>(regionAndMirror);
139 auto const& outSize = outputRegionForThread.GetSize();
140 std::vector<double> outTemp(outSize[m_ROW]*outSize[m_COL]);
142 std::vector<double> weights(outSize[m_ROW]*outSize[m_COL]);
144 typedef itk::ImageRegionConstIterator<InImageType> InIteratorType;
145 InIteratorType inIt(inputPtr, inputRegionForThread);
146 auto const& inputSize = inputRegionForThread.GetSize();
148 auto mirrorCol = inputSize[m_COL] + mirrorFirstCol + mirrorLastCol;
149 auto mirrorRow = inputSize[m_ROW] + mirrorFirstRow + mirrorLastRow;
150 InSizeType const& mirrorSize = {{mirrorCol, mirrorRow}};
152 std::vector<double> dataInput(mirrorSize[m_ROW]*mirrorSize[m_COL]);
154 for (
unsigned int row=
static_cast<unsigned int>(mirrorFirstRow);
155 row<static_cast<unsigned int>(mirrorFirstRow)+inputSize[m_ROW]; row++)
156 for (
unsigned int col=
static_cast<unsigned int>(mirrorFirstCol);
157 col<static_cast<unsigned int>(mirrorFirstCol)+inputSize[m_COL]; col++)
159 auto index = row * mirrorSize[m_COL] + col;
160 dataInput[index] =
static_cast<double>(inIt.Get());
167 for (
int row=0; row<mirrorFirstRow; row++)
169 int lineToCopy = (2*mirrorFirstRow - row)*mirrorSize[m_COL];
170 std::copy(dataInput.begin() + lineToCopy,
171 dataInput.begin() + lineToCopy + mirrorSize[m_COL],
172 dataInput.begin() + row*mirrorSize[m_COL] );
175 int lastRowRead = mirrorFirstRow+inputSize[m_ROW];
176 for (
int row=0; row<mirrorLastRow; row++)
178 int lineToCopy = (lastRowRead - row -2)*mirrorSize[m_COL];
179 std::copy(dataInput.begin() + lineToCopy,
180 dataInput.begin() + lineToCopy + mirrorSize[m_COL],
181 dataInput.begin() + (lastRowRead + row)*mirrorSize[m_COL]);
184 if (mirrorFirstCol > 0) {
185 for (
unsigned int row=0; row<mirrorSize[m_ROW]; row++)
187 std::reverse_copy(dataInput.begin() + row*mirrorSize[m_COL] + mirrorFirstCol+1,
188 dataInput.begin() + row*mirrorSize[m_COL] +2*mirrorFirstCol+1,
189 dataInput.begin() + row*mirrorSize[m_COL]);
194 if (mirrorLastCol > 0){
195 for (
unsigned int row=0; row<mirrorSize[m_ROW]; row++)
197 std::reverse_copy(dataInput.begin() + (row+1)*mirrorSize[m_COL] - 2*mirrorLastCol-1,
198 dataInput.begin() + (row+1)*mirrorSize[m_COL] - mirrorLastCol-1,
199 dataInput.begin() + (row+1)*mirrorSize[m_COL] - mirrorLastCol);
205 int fullMarginRow =
static_cast<int>(m_HalfSearchSize[m_ROW]+m_HalfPatchSize[m_ROW]);
206 int fullMarginCol =
static_cast<int>(m_HalfSearchSize[m_COL]+m_HalfPatchSize[m_COL]);
207 int searchSizeRow =
static_cast<int>(m_HalfSearchSize[m_ROW]);
208 int searchSizeCol =
static_cast<int>(m_HalfSearchSize[m_COL]);
211 auto const& inSize = outSize + sizeTwo * m_HalfPatchSize;
213 std::vector<double> imIntegral(inSize[m_ROW]*inSize[m_COL]);
214 for (
int drow=-searchSizeRow; drow < searchSizeRow+1; drow++)
215 for (
int dcol=-searchSizeCol; dcol < searchSizeCol+1; dcol++)
219 ComputeIntegralImage(dataInput, imIntegral, shift, inSize, mirrorSize);
221 for(
unsigned int row=0; row<outSize[m_ROW]; row++)
222 for (
unsigned int col=0; col<outSize[m_COL]; col++)
226 OutPixelType distance = ComputeDistance(row, col, imIntegral, inSize[m_COL]);
229 double weight = exp(
static_cast<double>(-distance));
230 outTemp[row*outSize[m_COL] + col] += weight*dataInput[(row+drow+fullMarginRow)*mirrorSize[m_COL]
231 + col+dcol+fullMarginCol];
232 weights[row*outSize[m_COL] + col] += weight;
238 typedef itk::ImageRegionIterator<OutImageType> OutputIteratorType;
240 OutputIteratorType outIt(outputPtr, outputRegionForThread);
242 for(
unsigned int index=0; index<outSize[m_ROW]*outSize[m_COL]; index++)
244 outIt.Set(
static_cast<OutPixelType>(outTemp[index]/weights[index]));
249 template<
class TInputImage,
class TOutputImage>
252 (
const std::vector<double> & dataInput,
253 std::vector<double> &imIntegral,
254 const OutIndexType shift,
const InSizeType sizeIntegral,
const InSizeType sizeInput)
const
262 auto const& offsetRef = m_HalfSearchSize;
263 OutSizeType
const& offsetShift = {{offsetRef[0] + shift[0], offsetRef[1] + shift[1]}};
266 auto indexInput = offsetRef[m_ROW]*sizeInput[m_COL] + offsetRef[m_COL];
267 auto indexShift = offsetShift[m_ROW]*sizeInput[m_COL] + offsetShift[m_COL];
268 double diff = dataInput[indexInput] - dataInput[indexShift];
269 imIntegral[0] = (diff * diff) - m_Var;
271 for (
unsigned int col=1; col<sizeIntegral[m_COL]; col++)
273 auto indexInputCol = indexInput + col;
274 auto indexShiftCol = indexShift + col;
275 diff = dataInput[indexInputCol] - dataInput[indexShiftCol];
276 double distance = diff * diff - m_Var;
277 imIntegral[col] = distance + imIntegral[col-1];
278 assert(imIntegral[col] < itk::NumericTraits<double>::max());
281 for (
unsigned int row=1; row<sizeIntegral[m_ROW]; row++)
283 auto indexInputRow = indexInput + row*sizeInput[m_COL];
284 auto indexShiftRow = indexShift + row*sizeInput[m_COL];
285 diff = dataInput[indexInputRow] - dataInput[indexShiftRow];
286 double distance = diff * diff - m_Var;
287 imIntegral[row*sizeIntegral[m_COL]] = distance + imIntegral[(row-1)*sizeIntegral[m_COL]];
288 assert(imIntegral[row*sizeIntegral[m_COL]] < itk::NumericTraits<double>::max());
292 for (
unsigned int row=1; row<sizeIntegral[m_ROW]; row++)
293 for (
unsigned int col=1; col<sizeIntegral[m_COL]; col++)
295 indexInput = (offsetRef[m_ROW]+row)*sizeInput[m_COL] + offsetRef[m_COL]+col;
296 indexShift = (offsetShift[m_ROW]+row)*sizeInput[m_COL] + offsetShift[m_COL]+col;
297 diff = dataInput[indexInput] - dataInput[indexShift];
298 double distance = diff*diff - m_Var;
299 imIntegral[row*sizeIntegral[m_COL] + col] = distance + imIntegral[row*sizeIntegral[m_COL] + col-1]
300 + imIntegral[(row-1)*sizeIntegral[m_COL] + col] - imIntegral[(row-1)*sizeIntegral[m_COL] + col-1];
301 assert(imIntegral[row*sizeIntegral[m_COL] + col] < itk::NumericTraits<double>::max());
306 template <
class TInputImage,
class TOutputImage>
308 NLMeansFilter<TInputImage, TOutputImage>::ComputeDistance
309 (
const unsigned int row,
const unsigned int col,
310 const std::vector<double>& imIntegral,
const unsigned int nbCols)
const
315 double distance_patch =
316 imIntegral[(row+2*m_HalfPatchSize[m_ROW])*nbCols + col+2*m_HalfPatchSize[m_COL]]
317 - imIntegral[row*nbCols + col+2*m_HalfPatchSize[m_COL]]
318 - imIntegral[(row+2*m_HalfPatchSize[m_ROW])*nbCols + col]
319 + imIntegral[row*nbCols + col];
321 distance_patch = std::max(distance_patch, 0.0) / (m_NormalizeDistance);
322 return static_cast<OutPixelType
>(distance_patch);
325 template<
class TInputImage,
class TOutputImage>
329 Superclass::PrintSelf(os, indent);
331 os<<indent<<
"NL Means Patch Size : "<<2*m_HalfPatchSize[m_ROW]+1
332 <<
" x "<<2*m_HalfPatchSize[m_COL]+1<< std::endl;
333 os<<indent<<
"NL Means Window Search Size : "<<2*m_HalfSearchSize[m_ROW]+1
334 <<
" x "<<2*m_HalfSearchSize[m_COL]+1<< std::endl;
335 os<<indent<<
"NL Means variance : "<<m_Var<<std::endl;
336 os<<indent<<
"NL Means threshold for similarity : "<<m_CutoffDistance