Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

(C++) DoFilterOperation

 

Graphics and math code Snippet to perform a filter operation (also called a convolution) on an image. This image is a 2D std::vector of int (y-x-ordered).

 

The tool FilterOperationer demonstrates the use of DoFilterOperation.

 

* View an example image of 'DoFilterOperation'.

* View the code of 'DoFilterOperation' in plain text.

 

 

#include <vector>

#include <cassert>

 

//Return a y-x-ordered 2D std::vector with the intensitief of grey

//values from range [0,255] (0=black,255=white) after the filter operation

//From http://www.richelbilderbeek.nl/CppDoFilterOperation.htm

const std::vector<std::vector<int> > DoFilterOperation(

const std::vector<std::vector<int> >& source, //y-x-ordered

const std::vector<std::vector<double> >& filter) //y-x-ordered

{

assert(!source.empty());

assert(!filter.empty());

const int width = source[0].size();

const int height = source.size();

std::vector<std::vector<int> > v(height, std::vector<int>(width));

const int maxx = source[0].size();

const int maxy = source.size();

const int midX = filter[0].size() / 2;

const int midY = filter.size() / 2;

 

const std::pair<double, double> filterRange = GetFilterRange(filter);

assert(filterRange.first < filterRange.second);

 

for (int y=0; y!=maxy; ++y)

{

const int writeY = y;

assert(writeY >= 0 && writeY < static_cast<int>(v.size()) );

std::vector<int>& vLine = v[writeY];

for (int x=0; x!=maxx; ++x)

{

//The x and y values are the topleft coordinate of where

// the filter will be applied to. This coordinat can be out of

// the range, but at least one pixel of where the filter will be

// applied to will be in range

//The pixel value is normalized to the area the

// filter operation took place on

//The outcome of the filter operation is written to

// (x + midX, y + midY), which HAS to be in range

const double unscaledPixelValue = GetFilterOperationPixel(source,x-midX,y-midY,filter);

//Scale the unscaledPixelValue.

//The maximal value of unscaledPixelValue is the sum of all positive

//values in the filter * 255.

//The minimum value of unscaledPixelValue is the sum of all negative

//values in the filter * 255.

//The scaled pixel value must be obtained by transforming the unscaled

//range [min,max] to [0,256>.

const double relUnscaledRange

= (unscaledPixelValue - filterRange.first)

/ (filterRange.second - filterRange.first);

assert(relUnscaledRange >= 0.0 && relUnscaledRange <= 1.0);

const double scaledRange = relUnscaledRange * 255;

assert(scaledRange >= 0.0 && scaledRange < 256.0);

const int pixel = scaledRange;

const int writeX = x;

assert(writeX >= 0 && writeX < width);

vLine[writeX] = pixel;

}

}

assert(source[0].size()==v[0].size());

assert(source.size()==v.size());

return v;

}

 

 

 

 

 

//The sourceX and sourceY values are the topleft coordinate of where

// the filter will be applied to. This coordinat can be out of

// the range, but at least one pixel of where the filter will be

// applied to will be in range. If there are no pixels in range,

// an assertion will fail.

//The pixel value is normalized to the area the

// filter operation took place on. Therefore, this area must be non-zero

//The outcome of this filter operation will be written to

// (x + midX, y + midY), which HAS to be in range

//From http://www.richelbilderbeek.nl/CppDoFilterOperation.htm

const double GetFilterOperationPixel(

const std::vector<std::vector<int> >& source, //y-x-ordered

const int sourceX,

const int sourceY,

const std::vector<std::vector<double> >& filter) //y-x-ordered

{

assert(!source.empty());

assert(!filter.empty());

const int sourceMaxY = source.size();

const int sourceMaxX = source[0].size();

const int filterMaxY = filter.size();

const int filterMaxX = filter[0].size();

 

double result = 0.0;

int nPixels = 0;

for (int y=0; y!=filterMaxY; ++y)

{

const int readY = sourceY + y;

if ( readY < 0 || readY >= sourceMaxY) continue;

assert(y >= 0);

assert(y < static_cast<int>(filter.size()));

const std::vector<double>& lineFilter = filter[y];

assert(readY >= 0);

assert(readY < static_cast<int>(source.size()));

const std::vector<int>& lineSource = source[readY];

for (int x=0; x!=filterMaxX; ++x)

{

const int readX = sourceX + x;

if ( readX < 0 || readX >= sourceMaxX) continue;

assert(x >= 0);

assert(x < filterMaxX);

assert(readX >= 0);

assert(readX < sourceMaxX);

const double deltaResult = static_cast<double>(lineSource[readX]) * lineFilter[x];

result += deltaResult;

++nPixels;

}

}

assert(nPixels!=0);

const double filteredValue = result / static_cast<double>(nPixels);

return filteredValue;

}

 

 

 

 

 

//Obtains the range a filter can have

//Assumes the every element has a maximum value of 255

//From http://www.richelbilderbeek.nl/CppDoFilterOperation.htm

const std::pair<double, double> GetFilterRange(const std::vector<std::vector<double> >& filter)

{

assert(!filter.empty());

const int maxx = filter[0].size();

const int maxy = filter.size();

assert(maxx + maxy > 2 && "Filter size must be bigger then 1x1");

double min = 0.0;

double max = 0.0;

for (int y=0; y!=maxy; ++y)

{

assert(y >= 0);

assert(y < static_cast<int>(filter.size()) );

 

const std::vector<double>& lineFilter = filter[y];

for (int x=0; x!=maxx; ++x)

{

assert(x >= 0);

assert(x < static_cast<int>(lineFilter.size()) );

 

const double value = lineFilter[x];

if (value < 0.0) min +=value;

else if (value > 0.0) max +=value;

}

}

const std::pair<double,double> range

= std::make_pair(min * 255.0, max * 255.0);

return range;

}

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.