Go back to Richel Bilderbeek's homepage.
Go back to Richel Bilderbeek's C++ page.
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.
//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);
{
assert(writeY >= 0 && writeY <
static_cast<int>(v.size())
);
std::vector<int>&
vLine = v[writeY];
{
//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>.
= (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;
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 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;
{
assert(y >= 0);
assert(y
< static_cast<int>(filter.size())
);
const std::vector<double>& lineFilter = filter[y];
{
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.