Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) FilterOperationerWindowsOnly

 

 

 

 

 

 

Technical facts

 

 

 

 

 

 

ProjectFilterOperationer.cpp

 

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("UnitFormFilterOperationer.cpp", FormFilterOperationer);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
        try
        {
                 Application->Initialize();
                 Application->Title = "FilterOperationer";
                 Application->CreateForm(__classid(TFormFilterOperationer), &FormFilterOperationer);
                 Application->Run();
        }
        catch (Exception &exception)
        {
                 Application->ShowException(&exception);
        }
        catch (...)
        {
                 try
                 {
                         throw Exception("");
                 }
                 catch (Exception &exception)
                 {
                         Application->ShowException(&exception);
                 }
        }
        return 0;
}
//---------------------------------------------------------------------------

 

 

 

 

 

UnitFormFilterOperationer.h

 

//---------------------------------------------------------------------------
/*
  FilterOperationer, performs filter operations on images
  Copyright (C) 2008  Richèl Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#ifndef UnitFormFilterOperationerH
#define UnitFormFilterOperationerH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <Graphics.hpp>
#include <Grids.hpp>
#include <Dialogs.hpp>
#include <ComCtrls.hpp>
#include <Menus.hpp>
//---------------------------------------------------------------------------
#include <iosfwd>
#include <vector>
//---------------------------------------------------------------------------
class TFormFilterOperationer : public TForm
{
__published: // IDE-managed Components
        TScrollBox *ScrollBoxOriginal;
        TScrollBox *ScrollBoxResult;
        TImage *ImageSource;
        TImage *ImageTarget;
        TStringGrid *StringGridFilter;
        TButton *ButtonDo;
        TButton *ButtonLoad;
        TButton *ButtonSaveResult;
        TOpenDialog *OpenDialog1;
        TSaveDialog *SaveDialog1;
        TButton *ButtonEnhance;
        TPageControl *PageControl;
        TTabSheet *TabSheetOriginal;
        TTabSheet *TabSheetFilter;
        TTabSheet *TabSheetResult;
        TTabSheet *TabSheetEnhancedResult;
        TPanel *PanelOriginal;
        TPanel *PanelResult;
        TPanel *PanelFilter;
        TPanel *PanelEnhanced;
        TButton *ButtonSaveEnhanced;
        TTabSheet *TabSheetAbout;
        TPopupMenu *PopupMenuFilter;
        TMenuItem *ItemAddCol;
        TMenuItem *ItemAddRow;
        TMenuItem *N1;
        TMenuItem *ItemRemoveCol;
        TMenuItem *ItemRemoveRow;
        TScrollBox *ScrollBoxEnhanced;
        TImage *ImageEnhanced;
        TRichEdit *RichEditLicence;
        TPanel *PanelAboutTop;
        TPanel *PanelAbout1;
        TPanel *PanelAbout2;
        TPanel *PanelAbout3;
        TPanel *PanelAbout6;
        TPanel *PanelAbout5;
        TPanel *PanelAbout4;
        TImage *ImageRichel;
        void __fastcall ButtonLoadClick(TObject *Sender);
        void __fastcall ButtonDoClick(TObject *Sender);
        void __fastcall ButtonSaveResultClick(TObject *Sender);
        void __fastcall ButtonEnhanceClick(TObject *Sender);
        void __fastcall ItemAddColClick(TObject *Sender);
        void __fastcall ItemAddRowClick(TObject *Sender);
        void __fastcall ItemRemoveColClick(TObject *Sender);
        void __fastcall ItemRemoveRowClick(TObject *Sender);
        void __fastcall ButtonSaveEnhancedClick(TObject *Sender);
        void __fastcall PanelAbout3Click(TObject *Sender);
        void __fastcall OnStringGridFilterChange(TObject *Sender);
        void __fastcall OnStringGridFilterChange2(TObject *Sender, WORD &Key,
          TShiftState Shift);
private: // User declarations
public: // User declarations
        __fastcall TFormFilterOperationer(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormFilterOperationer *FormFilterOperationer;
//---------------------------------------------------------------------------
//VCL dependent
export template <class T>
const std::vector<std::vector<T> > StringGridToVector(const TStringGrid * const stringGrid);

export template <class T>
const bool CanStringGridToVector(const TStringGrid * const stringGrid);

//VCL dependent
void DoFilterOperation(const TImage * const imageSource,
  const TStringGrid * const stringGrid,
  TImage * const target);

const bool CanDoFilterOperation(const TImage * const imageSource,
  const TStringGrid * const stringGridFilter,
  TImage * const imageTarget);

void EnhanceContrast(const TImage * const image);

const double GetAverageGreyness(const TImage * const image);


//Creates a 2D-std::vector (y-x-ordered) from a TImage
const std::vector<std::vector<int> > ImageToVector(const TImage * const image);

//Fills a TImage from a 2D-std::vector (y-x-ordered)
void VectorToImage(const std::vector<std::vector<int> >& v, const TImage * const image);

void Trace(const std::vector<std::string>& v);

void Trace(const std::string& s);

export template <class T>
void Trace(const std::vector<std::vector<T> >& v);

void Trace(const std::vector<std::vector<int> >& v);


//VCL independent
/*
const std::vector<std::vector<int> >
  DoFilterOperation(
    const std::vector<std::vector<int> >& imageSource,
    const std::vector<std::vector<double> >& filter);
*/

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


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

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

//const std::vector<std::vector<int> > GetRescaledImage(
//  const std::vector<std::vector<int> >& source);

//const std::vector<std::vector<int> > GetRescaledImage(
//  const std::vector<std::vector<int> >& source,
//  const std::vector<std::vector<double> >& filter);



//export template <class T>
//const double GetAverage(const std::vector<std::vector<T> >& v);

//From http://www.richelbilderbeek.nl/CppDoHistogramEqualization.htm
void DoHistogramEqualization(const TImage * const source, TImage * const target);

//From htpp://www.richelbilderbeek.nl/CppGetCumulativeHistogram.htm
template <class T> const std::vector<T> GetCumulativeHistogram(const std::vector<T>& histogram);

//From htpp://www.richelbilderbeek.nl/CppGetImageHistogram.htm
const std::vector<int> GetImageHistogram(const TImage * const image);

//From http://www.richelbilderbeek.nl/CppCanCast.htm
template <class TargetType> const bool CanCast(const std::string& from);

//From http://www.richelbilderbeek.nl/CppLexicalCast.htm
template <typename TargetType>
const TargetType LexicalCast(const std::string& s);

//From http://www.richelbilderbeek.nl/CppLexicalCast.htm
export template <>
const int LexicalCast<int>(const std::string& s);

//From http://www.richelbilderbeek.nl/CppLexicalCast.htm
export  template <>
const double LexicalCast<double>(const std::string& s);


#endif

 

 

 

 

 

UnitFormFilterOperationer.cpp

 

//---------------------------------------------------------------------------
/*
  FilterOperationer, performs filter operations on images
  Copyright (C) 2008  Richèl Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#include <cmath>
#include <algorithm>
#include <functional>
#include <string>
#include <sstream>
#include <cassert>
#include <vcl.h>
#pragma hdrstop

#include "UnitFormFilterOperationer.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormFilterOperationer *FormFilterOperationer;
//---------------------------------------------------------------------------
__fastcall TFormFilterOperationer::TFormFilterOperationer(TComponent* Owner)
        : TForm(Owner)
{
  //A vertical edge detection filter
  StringGridFilter->Cells[0][0] = -1.0;
  StringGridFilter->Cells[1][0] =  0.0;
  StringGridFilter->Cells[2][0] =  1.0;
  StringGridFilter->Cells[0][1] = -1.0;
  StringGridFilter->Cells[1][1] =  0.0;
  StringGridFilter->Cells[2][1] =  1.0;
  StringGridFilter->Cells[0][2] = -1.0;
  StringGridFilter->Cells[1][2] =  0.0;
  StringGridFilter->Cells[2][2] =  1.0;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ButtonLoadClick(TObject *Sender)
{
  if (OpenDialog1->Execute()==false) return;
  ImageSource->Picture->LoadFromFile(OpenDialog1->FileName);
  ImageSource->Visible = true;
  OnStringGridFilterChange(0);
  ++PageControl->ActivePageIndex;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::OnStringGridFilterChange2(
      TObject *Sender, WORD &Key, TShiftState Shift)
{
  OnStringGridFilterChange(0);
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::OnStringGridFilterChange(
      TObject *Sender)
{
  ButtonDo->Enabled = CanDoFilterOperation(ImageSource, StringGridFilter, ImageTarget);
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ButtonDoClick(TObject *Sender)
{
  //Check StringGrid for valid strings
  assert(CanDoFilterOperation(ImageSource, StringGridFilter, ImageTarget)==true);

  //Convert StringGrid to std::vector and then the other way round,
  //to clean up the StringGrid after lousy input
  {
    const std::vector<std::vector<double> > v = StringGridToVector<double>(StringGridFilter);
    assert(v.empty()==false);
    const int maxy = v.size();
    assert(maxy>0);
    const int maxx = v[0].size();
    assert(maxx>0);
    for (int y=0; y!=maxy; ++y)
    {
      for (int x=0; x!=maxx; ++x)
      {
        StringGridFilter->Cells[x][y] = v[y][x];
      }
    }
  }

  DoFilterOperation(ImageSource, StringGridFilter, ImageTarget);
  ImageTarget->Visible = true;
  ImageTarget->Refresh();
  ButtonSaveResult->Enabled = true;
  ButtonEnhance->Enabled = true;
  ++PageControl->ActivePageIndex;

}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ButtonEnhanceClick(TObject *Sender)
{
  ImageEnhanced->Picture->Graphic = ImageTarget->Picture->Graphic;

  #ifndef NDEBUG
    const double averageGreynessOld = GetAverageGreyness(ImageEnhanced);
  #endif

  EnhanceContrast(ImageEnhanced);

  #ifndef NDEBUG
    const double averageGreynessNew = GetAverageGreyness(ImageEnhanced);
    assert( std::fabs(averageGreynessOld - averageGreynessNew) < 1.0 );
  #endif

  ImageEnhanced->Visible = true;
  ImageEnhanced->Refresh();
  ButtonSaveEnhanced->Enabled = true;
  ++PageControl->ActivePageIndex;

}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ButtonSaveResultClick(TObject *Sender)
{
  if (SaveDialog1->Execute()==false) return;
  ImageTarget->Picture->SaveToFile(SaveDialog1->FileName);
  ShowMessage("Image saved successfully");
  PageControl->ActivePageIndex+=2;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ItemAddColClick(TObject *Sender)
{
  ++StringGridFilter->ColCount;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ItemAddRowClick(TObject *Sender)
{
  ++StringGridFilter->RowCount;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ItemRemoveColClick(TObject *Sender)
{
  if (StringGridFilter->ColCount > 1) --StringGridFilter->ColCount;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ItemRemoveRowClick(TObject *Sender)
{
  if (StringGridFilter->RowCount > 1) --StringGridFilter->RowCount;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::ButtonSaveEnhancedClick(
      TObject *Sender)
{
  if (SaveDialog1->Execute()==false) return;
  ImageEnhanced->Picture->SaveToFile(SaveDialog1->FileName);
  ShowMessage("Image saved successfully");
  ++PageControl->ActivePageIndex;
}
//---------------------------------------------------------------------------
void __fastcall TFormFilterOperationer::PanelAbout3Click(TObject *Sender)
{
  ImageSource->Picture = ImageRichel->Picture;
  ImageSource->Refresh();
  ImageSource->Visible = true;
  ButtonDo->Enabled = true;
  PageControl->ActivePageIndex = 0;
}
//---------------------------------------------------------------------------
const bool CanDoFilterOperation(const TImage * const imageSource,
  const TStringGrid * const stringGridFilter,
  TImage * const imageTarget)
{
  if (imageSource == 0) return false;
  if (imageSource->Picture->Bitmap == 0) return false;
  if (imageSource->Picture->Bitmap->PixelFormat != pf24bit) return false;
  if (stringGridFilter == 0) return false;
  if (imageTarget == 0) return false;
  if (imageTarget->Picture->Bitmap == 0) return false;
  if (imageTarget->Picture->Bitmap->PixelFormat != pf24bit) return false;
  if (stringGridFilter->ColCount < 1) return false;
  if (stringGridFilter->RowCount < 1) return false;
  if (stringGridFilter->ColCount == 1 && stringGridFilter->RowCount == 1) return false;

  if (CanStringGridToVector<double>(stringGridFilter) == false) return false;

  //Does the filter yield a valid filter range?
  const std::vector<std::vector<double> > filter = StringGridToVector<double>(stringGridFilter);
  const std::pair<double,double> filterRange = GetFilterRange(filter);
  if (filterRange.first == filterRange.second) return false;
  return true;
}
//---------------------------------------------------------------------------
void DoFilterOperation(const TImage * const imageSource,
  const TStringGrid * const stringGridFilter,
  TImage * const imageTarget)
{
  assert(imageSource != 0
    && "imageSource must not be NULL");
  assert(imageSource->Picture->Bitmap != 0
    && "imageSource bitmap must not be NULL");
  assert(imageSource->Picture->Bitmap->PixelFormat == pf24bit
    && "imageSource must be a 24-bit bitmap");
  assert(stringGridFilter != 0
    && "stringGridFilter must not be NULL");
  assert(imageTarget != 0
    && "imageTarget must not be NULL");
  assert(imageTarget->Picture->Bitmap != 0
    && "imageSource bitmap must not be NULL");
  assert(imageTarget->Picture->Bitmap->PixelFormat == pf24bit
    && "imageTarget must be a 24-bit bitmap");
  assert(stringGridFilter->ColCount >= 1
    && "stringGridFilter must have at least one collumn");
  assert(stringGridFilter->RowCount >= 1
    && "stringGridFilter must have at least one row");
  assert( (stringGridFilter->ColCount > 1 || stringGridFilter->RowCount > 1)
    && "stringGridFilter must be larger then 1x1 cell");
  assert(CanStringGridToVector<double>(stringGridFilter) == true
    && "stringGridFilter's content must be convertable to doubles");

  const std::vector<std::vector<int> > source = ImageToVector(imageSource);

  const std::vector<std::vector<double> > filter = StringGridToVector<double>(stringGridFilter);

  assert(!source.empty());
  assert(!filter.empty());

  #ifndef NDEBUG
  {
    const std::pair<double,double> filterRange = GetFilterRange(filter);
    assert(filterRange.first != filterRange.second && "Filter must have a valid range");
  }
  #endif

  const std::vector<std::vector<int> > result = DoFilterOperation(source,filter);
  VectorToImage(result,imageTarget);
}
//---------------------------------------------------------------------------
//VCL dependent
//From http://www.richelbilderbeek.nl/CppStringGridToVector.htm
template <class T>
const std::vector<std::vector<T> > StringGridToVector(const TStringGrid * const stringGrid)
{
  assert(CanStringGridToVector<T>(stringGrid)==true);
  assert(stringGrid!=0 && "StringGrid must not be NULL");
  const int height = stringGrid->RowCount;
  const int width  = stringGrid->ColCount;
  std::vector<std::vector<T> > v(height,std::vector<T>(width));
  for (int y=0; y!=height; ++y)
  {
    assert(y >=0);
    assert(y < static_cast<int>(v.size()) );
    std::vector<T>& line = v[y];
    //Don't have the guts to do a line-access on a TStringGrid...

    for (int x=0; x!=width; ++x)
    {
      assert(x >=0);
      assert(x < static_cast<int>(line.size()) );
      //const_cast because the VCL is not const-correct. Grumble, grumble...
      const std::string s = (const_cast<TStringGrid*>(stringGrid)->Cells[x][y]).c_str();
      const T t = LexicalCast<T>(s);
      line[x] = t;
    }
  }
  return v;
}
//---------------------------------------------------------------------------
//VCL dependent
//From http://www.richelbilderbeek.nl/CanStringGridToVector.htm
template <class T>
const bool CanStringGridToVector(const TStringGrid * const stringGrid)
{
  if (stringGrid==0) return false;

  const int height = stringGrid->RowCount;
  const int width  = stringGrid->ColCount;

  for (int y=0; y!=height; ++y)
  {
    for (int x=0; x!=width; ++x)
    {
      //const_cast because the VCL is not const-correct. Grumble, grumble...
      const std::string s = (const_cast<TStringGrid*>(stringGrid)->Cells[x][y]).c_str();
      if (CanCast<T>(s) == false) return false;
    }
  }
  return true;
}
//---------------------------------------------------------------------------
void Trace(const std::vector<std::string>& v)
{
  #ifndef NTRACE
    const int size = v.size();
    for (int i=0; i!=size; ++i)
    {
      OutputDebugString( v[i].c_str() );
    }
  #endif
}
//---------------------------------------------------------------------------
void Trace(const std::string& s)
{
  #ifndef NTRACE
    OutputDebugString(s.c_str());
  #endif
}
//---------------------------------------------------------------------------
export template <class T>
void Trace(const std::vector<std::vector<T> >& v)
{
  #ifndef NTRACE
    if (v.empty() == true) { OutputDebugString("{empty}"); return; }
    const int maxy = v.size();
    const int maxx = v[0].size();
    for (int y=0; y!=maxy; ++y)
    {
      const std::vector<T>& vLine = v[y];
      std::string s;
      for (int x=0; x!=maxx; ++x)
      {
        s+=boost::lexical_cast<std::string>(vLine[x]) + std::string(",");
      }
      s.pop_back(); //Remove last comma
      OutputDebugString(s.c_str());
    }
  #endif
}
//---------------------------------------------------------------------------
void Trace(const std::vector<std::vector<int> >& v)
{
  #ifndef NTRACE
    if (v.empty() == true) { OutputDebugString("{empty}"); return; }
    const int maxy = v.size();
    const int maxx = v[0].size();
    for (int y=0; y!=maxy; ++y)
    {
      const std::vector<int>& vLine = v[y];
      std::string s;
      for (int x=0; x!=maxx; ++x)
      {
        s+=boost::lexical_cast<std::string>(static_cast<int>(vLine[x])) + std::string(",");
      }
      s.pop_back(); //Remove last comma
      OutputDebugString(s.c_str());
    }
  #endif
}
//---------------------------------------------------------------------------
//VCL independent
//Creates a 2D-std::vector (y-x-ordered) from a TImage
//From http://www.richelbilderbeek.nl/CppImageToVector.htm
const std::vector<std::vector<int> > ImageToVector(const TImage * const image)
{
  assert(image!=0 && "Image must not be NULL");
  assert(image->Picture->Bitmap!=0 && "Bitmap must not be NULL");
  assert(image->Picture->Bitmap->PixelFormat == pf24bit && "Bitmap must be 24 bit");

  const int width  = image->Picture->Bitmap->Width;
  const int height = image->Picture->Bitmap->Height;

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

  for (int y=0; y!=height; ++y)
  {
    assert(y >= 0);
    assert(y < static_cast<int>(v.size()));
    std::vector<int>& vLine = v[y];
    const unsigned char * const line
      = static_cast<const unsigned char *>(image->Picture->Bitmap->ScanLine[y]);
    for (int x=0; x!=width; ++x)
    {
      assert(x >= 0);
      assert(x < static_cast<int>(vLine.size()));
      const double grey = static_cast<double>(line[x*3+0] + line[x*3+1] + line[x*3+2])
        / 3.0;
      vLine[x] = grey;
      assert(grey>=0.0 && grey<256.0);
    }
  }
  return v;
}
//---------------------------------------------------------------------------
//VCL independent
//Creates a 2D-std::vector (y-x-ordered) from a TImage
//Draws a (grey) TImage from a 2D-std::vector (y-x-ordered)
//From http://www.richelbilderbeek.nl/CppVectorToImage.htm
void VectorToImage(const std::vector<std::vector<int> >& v, const TImage * const image)
{
  assert(image!=0 && "Image must not be NULL");
  assert(image->Picture->Bitmap!=0 && "Bitmap must not be NULL");
  assert(image->Picture->Bitmap->PixelFormat == pf24bit && "Bitmap must be 24 bit");

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

  image->Picture->Bitmap->Height = height;
  image->Picture->Bitmap->Width  = width;

  for (int y=0; y!=height; ++y)
  {
    assert(y >= 0);
    assert(y < static_cast<int>(v.size()));
    const std::vector<int>& vLine = v[y];
    unsigned char * const line
      = static_cast<unsigned char *>(image->Picture->Bitmap->ScanLine[y]);
    for (int x=0; x!=width; ++x)
    {
      assert(x >= 0);
      assert(x < static_cast<int>(vLine.size()));
      const int grey = vLine[x];
      line[x*3+2] = grey; //Red
      line[x*3+1] = grey; //Green
      line[x*3+0] = grey; //Blue
    }
  }
}
//---------------------------------------------------------------------------
//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;
}
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppEnhanceContrast.htm
void EnhanceContrast(const TImage * const image)
{
  //Find lowest and highest value
  assert(image!=0 && "image must not be NULL");
  assert(image->Picture->Bitmap!=0 && "image bitmap must not be NULL");
  assert(image->Picture->Bitmap->PixelFormat == pf24bit && "image bitmap must be 24 bit");

  //Get the width and height from the source
  const int width  = image->Picture->Bitmap->Width;
  const int height = image->Picture->Bitmap->Height;
  const double averageGreyness = GetAverageGreyness(image);

  double stretch = 1000.0; //Way too much for nearly all images

  for (int y=0; y!=height; ++y)
  {
    const unsigned char * const line
      = static_cast<const unsigned char *>(
        image->Picture->Bitmap->ScanLine[y]);
    for (int x=0; x!=width; ++x)
    {
      const double grey
        = static_cast<double>(line[x*3+0] + line[x*3+1] + line[x*3+2])
        / 3.0;
      const double diff = grey - averageGreyness;
      if (diff < 0.0)
      {
        //pixel was darker then average
        const double newStretch = -averageGreyness / diff;
        assert(newStretch >= 0.0);
        if (newStretch < stretch) stretch = newStretch;
      }
      else if (diff > 0.0)
      {
        //pixel was lighter then average
        const double newStretch = (255.9 - averageGreyness)  / diff;
        if (newStretch < stretch) stretch = newStretch;
      }
    }
  }

  //Enhance the contrast
  for (int y=0; y!=height; ++y)
  {
    unsigned char * const line
      = static_cast<unsigned char *>(
        image->Picture->Bitmap->ScanLine[y]);
    for (int x=0; x!=width; ++x)
    {
      const double grey
        = static_cast<double>(line[x*3+0] + line[x*3+1] + line[x*3+2])
        / 3.0;
      const double diff = grey - averageGreyness;
      const double diffGreyNew = averageGreyness + (static_cast<double>(diff) * stretch);
      const int newGrey = static_cast<int>(diffGreyNew);
      assert(newGrey >=   0);
      assert(newGrey <  256);
      line[x*3+2] = newGrey; //Red
      line[x*3+1] = newGrey; //Green
      line[x*3+0] = newGrey; //Blue
    }
  }
}

//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppGetAverageGreyness.htm
const double GetAverageGreyness(const TImage * const image)
{
  assert(image!=0 && "image must not be NULL");
  assert(image->Picture->Bitmap!=0 && "image bitmap must not be NULL");
  assert(image->Picture->Bitmap->PixelFormat == pf24bit && "image bitmap must be 24 bit");

  //Get the width and height from the source
  const int width  = image->Picture->Bitmap->Width;
  const int height = image->Picture->Bitmap->Height;
  double sum = 0.0;

  for (int y=0; y!=height; ++y)
  {
    assert(y >= 0);
    assert(y < image->Picture->Bitmap->Height);
    const unsigned char * line
      = static_cast<const unsigned char *>(
        image->Picture->Bitmap->ScanLine[y]);
    for (int x=0; x!=width; ++x)
    {
      assert(x >= 0);
      assert(x < image->Picture->Bitmap->Width);
      const double grey
        = static_cast<double>(line[x*3+0] + line[x*3+1] + line[x*3+2])
        / 3.0;
      sum += grey;
    }
  }
  const int surface = width * height;
  assert(surface>0);
  const double average = sum / static_cast<double>(surface);
  assert(average >=    0.0);
  assert(average  <  256.0);
  return average;
}
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppCanCast.htm
template <class TargetType> const bool CanCast(const std::string& from)
{
  std::istringstream i(from);
  TargetType temp;
  return ( (i >> temp) ? true : false );
}
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppLexicalCast.htm
template <>
const int LexicalCast<int>(const std::string& s)
{
  assert(CanCast<int>(s) == true);
  return std::atoi(s.c_str());
}
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppLexicalCast.htm
template <>
const double LexicalCast<double>(const std::string& s)
{
  assert(CanCast<double>(s) == true);
  return std::atof(s.c_str());
}
//---------------------------------------------------------------------------













 

 

 

 

 

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

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml