Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) ImageToStringList

 

ImageToStringList is a VCL graphics code snippet for the convertion of a bitmap to an ASCII-art image, in which the bitmap is stored in a CLX or VCL TImage respecively.

 

Note how I love using assert to prevent any trivial error. It also saves me many questions from students.

 

 

 

 

 

CLX

#include <memory>
#include <algorithm>
#include <vector>
#include <cassert>
#include <Classes.hpp>
//---------------------------------------------------------------------------
//Creates a std::vector that holds the ASCII art characters
//ordered from dark to light
std::vector<char> GetChars()
{
  std::vector<char> chars;
  chars.push_back('M');
  chars.push_back('N');
  chars.push_back('m');
  chars.push_back('d');
  chars.push_back('h');
  chars.push_back('y');
  chars.push_back('s');
  chars.push_back('o');
  chars.push_back('+');
  chars.push_back('/');
  chars.push_back(':');
  chars.push_back('-');
  chars.push_back('.');
  chars.push_back('`');
  chars.push_back(' ');
  return chars;
}
//---------------------------------------------------------------------------
//Generalizes a rectangle to one grey value
double GetFractionGrey(
  const TImage* const image,
  const int x1,
  const int y1,
  const int x2,
  const int y2)
{
  assert(image!=0);
  assert(image->Picture->Bitmap != 0);
  assert(image->Picture->Bitmap->PixelFormat == pf32bit);
  assert(x1 < image->Picture->Bitmap->Width);
  assert(y1 < image->Picture->Bitmap->Height);
  assert(x2 <= image->Picture->Bitmap->Width);
  assert(y2 <= image->Picture->Bitmap->Height);
  double sumGrey = 0.0;
  int nPixels = 0;
  for (int y=y1; y!=y2; ++y)
  {
    assert(y < image->Picture->Bitmap->Height);
    const unsigned char * line
      = static_cast<const unsigned char *>(image->Picture->Bitmap->ScanLine[y]);
    for (int x=x1; x!=x2; ++x)
    {
      assert(x < image->Picture->Bitmap->Width);
      //Note that in the line below, line[x*4+3] is not used.
      //I guess that this is the alpha-blending value?
      const int sumPixel = line[x*4+0] + line[x*4+1] + line[x*4+2];
      assert( sumPixel < 3 * 256 );
      const double grey
        = static_cast<double>(sumPixel)
        / (3.0 * 255.0);
      sumGrey+=grey;
      ++nPixels;
    }
  }
  if (nPixels == 0)
  {
    //When the value is trivial, return a random value...
    //Will not occur when nChars >= 5
    return std::rand()%256;
  }
  else
  {
    const double fractionGrey = sumGrey / static_cast<double>(nPixels);
    return fractionGrey;
  }
}
//---------------------------------------------------------------------------
TStringList * ImageToAscii(
  const TImage* const image,
  const int charWidth) //How many chars the ASCII image will be wide
{
  static const std::vector<char> chars(GetChars());

  //If the number of chars is below 5,
  //the calculation would be more complicated due to a too trivial value
  assert(charWidth >= 5);

  //Check the bitmap
  assert(image->Picture->Bitmap != 0);
  assert(image->Picture->Bitmap->PixelFormat == pf32bit);

  //Maxy is in proportion with the bitmap
  const int maxy =
    (static_cast<double>(charWidth)
    / static_cast<double>(image->Picture->Bitmap->Width))
    * static_cast<double>(image->Picture->Bitmap->Height);
  assert(charWidth > 0);
  assert(maxy > 0);
  const double dX = static_cast<double>(image->Picture->Bitmap->Width)
    / static_cast<double>(charWidth);
  const double dY = static_cast<double>(image->Picture->Bitmap->Height)
    / static_cast<double>(maxy);
  assert(dX > 0.0);
  assert(dY > 0.0);

  TStringList * stringList = new TStringList;

  for (int y=0; y!=maxy; ++y)
  {
    String myLine = "";
    for (int x=0; x!=charWidth; ++x)
    {
      const int x1 = std::min(
        static_cast<double>(x) * dX,
        image->Picture->Bitmap->Width - 1.0) + 0.5;
      const int y1 = std::min(
        static_cast<double>(y) * dY,
        image->Picture->Bitmap->Height - 1.0) + 0.5;
      const int x2 = std::min(
        (static_cast<double>(x) * dX) + dX,
        image->Picture->Bitmap->Width - 1.0) + 0.5;
      const int y2 = std::min(
        (static_cast<double>(y) * dY) + dY,
        image->Picture->Bitmap->Height - 1.0) + 0.5;
      assert(x1 >= 0);
      assert(x2 >= 0);
      assert(y1 >= 0);
      assert(y2 >= 0);
      assert(x1 < image->Picture->Bitmap->Width);
      assert(x2 < image->Picture->Bitmap->Width);
      assert(y1 < image->Picture->Bitmap->Height);
      assert(y2 < image->Picture->Bitmap->Height);

      const double fGrey = std::min(std::max(0.0,
        GetFractionGrey(image,x1,y1,x2,y2)),1.0);
      assert(fGrey >= 0.0 && fGrey <= 1.0);
      const double charIndex = fGrey
        * static_cast<double>(chars.size() - 1);
      assert(charIndex >= 0);
      assert(charIndex < static_cast<int>(chars.size()));
      const char thisChar = chars[charIndex];
      myLine+=thisChar;
    }
    stringList->Add(myLine);
  }
  return stringList;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  const std::auto_ptr<TStringList> ascii(
    ImageToAscii(Image1,Image1->Picture->Bitmap->Width));
  Memo1->Lines = ascii.get();
}
//---------------------------------------------------------------------------

 

 

 

 

 

VCL

 

#include <memory>
#include <algorithm>
#include <vector>
#include <cassert>
#include <Classes.hpp>
//---------------------------------------------------------------------------
//Creates a std::vector that holds the ASCII art characters
//ordered from dark to light
std::vector<char> GetChars()
{
  std::vector<char> chars;
  chars.push_back('M');
  chars.push_back('N');
  chars.push_back('m');
  chars.push_back('d');
  chars.push_back('h');
  chars.push_back('y');
  chars.push_back('s');
  chars.push_back('o');
  chars.push_back('+');
  chars.push_back('/');
  chars.push_back(':');
  chars.push_back('-');
  chars.push_back('.');
  chars.push_back('`');
  chars.push_back(' ');
  return chars;
}
//---------------------------------------------------------------------------
//Generalizes a rectangle to one average greyness
double GetFractionGrey(
  const TImage* const image,
  const int x1,
  const int y1,
  const int x2,
  const int y2)
{
  assert(image!=0);
  assert(image->Picture->Bitmap != 0);
  assert(image->Picture->Bitmap->PixelFormat == pf24bit);

  assert(x1 < image->Picture->Bitmap->Width);
  assert(y1 < image->Picture->Bitmap->Height);
  assert(x2 <= image->Picture->Bitmap->Width);
  assert(y2 <= image->Picture->Bitmap->Height);
  double sumGrey = 0.0;
  int nPixels = 0;
  for (int y=y1; y!=y2; ++y)
  {
    assert(y < image->Picture->Bitmap->Height);
    const unsigned char * line
      = static_cast<const unsigned char *>(image->Picture->Bitmap->ScanLine[y]);
    for (int x=x1; x!=x2; ++x)
    {
      assert(x < image->Picture->Bitmap->Width);
      const int sumPixel = line[x*3+0] + line[x*3+1] + line[x*3+2];
      assert( sumPixel < 3 * 256 );
      const double grey =
        static_cast<double>(sumPixel)
        / (3.0 * 255.0);
      sumGrey+=grey;
      ++nPixels;
    }
  }
  if (nPixels == 0)
  {
    //When the value is trivial, return a random value...
    //Will not occur when nChars >= 5
    return std::rand()%256;
  }
  else
  {
    const double fractionGrey = sumGrey / static_cast<double>(nPixels);
    return fractionGrey;
  }
}
//---------------------------------------------------------------------------
TStringList * ImageToAscii(
  const TImage* const image,
  const int charWidth) //How many chars the ASCII image will be wide
{
  static const std::vector<char> chars(GetChars());

  //If the number of chars is below 5,
  //the calculation would be more complicated due to a too trivial value
  assert(charWidth >= 5);

  //Check the bitmap
  assert(image->Picture->Bitmap != 0);
  assert(image->Picture->Bitmap->PixelFormat == pf24bit);


  //Maxy is in proportion with the bitmap
  const int maxy =
    (static_cast<double>(charWidth)
    / static_cast<double>(image->Picture->Bitmap->Width))
    * static_cast<double>(image->Picture->Bitmap->Height);
  assert(charWidth > 0);
  assert(maxy > 0);
  const double dX = static_cast<double>(image->Picture->Bitmap->Width)
    / static_cast<double>(charWidth);
  const double dY = static_cast<double>(image->Picture->Bitmap->Height)
    / static_cast<double>(maxy);
  assert(dX > 0.0);
  assert(dY > 0.0);


  TStringList * stringList = new TStringList;

  for (int y=0; y!=maxy; ++y)
  {
    String myLine = "";
    for (int x=0; x!=charWidth; ++x)
    {
      const int x1 = std::min(
        static_cast<double>(x) * dX,
        image->Picture->Bitmap->Width - 1.0) + 0.5;
      const int y1 = std::min(
        static_cast<double>(y) * dY,
        image->Picture->Bitmap->Height - 1.0) + 0.5;
      const int x2 = std::min(
        (static_cast<double>(x) * dX) + dX,
        image->Picture->Bitmap->Width - 1.0) + 0.5;
      const int y2 = std::min(
        (static_cast<double>(y) * dY) + dY,
        image->Picture->Bitmap->Height - 1.0) + 0.5;
      assert(x1 >= 0);
      assert(x2 >= 0);
      assert(y1 >= 0);
      assert(y2 >= 0);
      assert(x1 < image->Picture->Bitmap->Width);
      assert(x2 < image->Picture->Bitmap->Width);
      assert(y1 < image->Picture->Bitmap->Height);
      assert(y2 < image->Picture->Bitmap->Height);

      const double fGrey = std::min(
        std::max(0.0, GetFractionGrey(image,x1,y1,x2,y2)),1.0);
      assert(fGrey >= 0.0 && fGrey <= 1.0);
      const double charIndex
        = fGrey * static_cast<double>(chars.size() - 1);
      assert(charIndex >= 0);
      assert(charIndex < static_cast<int>(chars.size()));
      const char thisChar = chars[charIndex];
      myLine+=thisChar;
    }
    stringList->Add(myLine);
  }
  return stringList;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  const std::auto_ptr<TStringList> ascii(
    ImageToAscii(Image1,Image1->Picture->Bitmap->Width));
  RichEdit1->Lines = ascii.get();
}
//---------------------------------------------------------------------------

 

 

 

 

 

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

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict