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.
#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; }