реферат Программа распознавания символов

Белорусский Государственный Университет Информатики и Радиоэлектроники.



                             Контрольная работа
                                по дисциплине
                                   «МАГИ»

                     «Программа распознавания символов»



                                          Выполнил студент группы 500501
                                          Балахонов Е.В.

                                  Задание.


      Требуется  написать  программу,  способную   распознавать   графически
представленные символы в виде растрового  изображения  и  преобразовывать  в
обычный текст.
      - платформа: Win32,
      - формат графического изображения: Windows Bitmap (BMP), 8 бит,
      - шрифт для распознавания: Arial, 16



                          Выбор средств разработки.


      В качестве среды разработки будет использоваться Borland  C++  Builder
5.


                           Распознавание символов.



Этап 1. Выделение контура объекта, определение его границ.


      В качестве алгоритма выделения контуров  будем  использовать  алгоритм
жука.

Общее описание алгоритма.



     Отслеживающие  алгоритмы  основаны  на   том,   что   на   изображении
отыскивается объект (первая встретившаяся точка объекта)  и  контур  объекта
отслеживается и векторизуется. Достоинством данных  алгоритмов  является  их
простота, к недостаткам  можно  отнести  их  последовательную  реализацию  и
некоторую сложность при  поиске  и  обработке  внутренних  контуров.  Пример
отслеживающего алгоритма - "алгоритма жука" - приведен  на  рис.  5.12.  Жук
начинает движение с белой области по направлению к  черной,  Как  только  он
попадает на черный элемент, он поворачивает налево и переходит к  следующему
элементу. Если этот элемент белый, то жук поворачивается  направо,  иначе  -
налево. Процедура повторяется до тех пор, пока жук не  вернется  в  исходную
точку. Координаты точек перехода с черного на белое и с белого на  черное  и
описывают границу объекта.

     На рис. 1 показана схема работы такого алгоритма.
[pic]
            Рис. 1. Схема работы отслеживающего алгоритма «жука».



Этап 2. Построение на основе контура объекта скелетной линии.


      При нахождении новой точки контура,  рассчитывается  расстояние  между
предыдущей найденной точкой и новой. Если оно  превышает  некоторую  границу
(по умолчанию в 5 единиц),  она запоминается. К концу  построения  скелетной
линии программа имеет массив  координат  вершин  ломаной,  которая  является
скелетной линией объекта.


Этап 3. Сравнение полученной скелетной линии с списком шаблонов.


      После построения скелетной линии производится сравнение ее с списком
шаблонов известных символов. При нахождении совпадения, программа
записывает в строку найденный символ.



                          Исходный текст программы.


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

#include <vcl.h>
#pragma hdrstop

#include <math.h>
#include <fstream.h>

#include "ChildFormUnit.h"
#include "MainFormUnit.h"
#include "AverageFilterDialogFormUnit.h"
#include "OSRFormUnit.h"

//--------------------------------------------------------------------------
-
#pragma package(smart_init)
#pragma resource "*.dfm"
TChildForm *ChildForm;
TTemplates Templates;
//--------------------------------------------------------------------------
-
__fastcall TChildForm::TChildForm(TComponent* Owner)
        : TForm(Owner)
{
}
//--------------------------------------------------------------------------
-
bool __fastcall TChildForm::LoadImage(AnsiString FileName)
{
 try
 {
  Image1->Picture->LoadFromFile(FileName);
 }
 catch (EInvalidGraphic& Exception)
 {
  AnsiString Error = "Ошибка загрузки файла изображения! Ошибка системы: ";
  Error += Exception.Message;
  MessageBox(this->Handle, Error.c_str(), "Ошибка", MB_OK | MB_ICONERROR);
  return false;
 }

 if (Image1->Picture->Bitmap->PixelFormat != pf8bit)
 {
  MessageBox(Handle,"Такой формат файла пока не подерживается...",
                    "Слабоват я пока...",MB_OK | MB_ICONSTOP |
MB_APPLMODAL);
  return false;
 }

 return true;
}
//--------------------------------------------------------------------------
-
void __fastcall TChildForm::FormClose(TObject *Sender,
      TCloseAction &Action)
{
 MainForm->DeleteActiveChildForm();
}
//--------------------------------------------------------------------------
-
void __fastcall TChildForm::AverageFilter()
{
 AverageFilterDialogForm = new TAverageFilterDialogForm(this);
 if (AverageFilterDialogForm->ShowModal() == mrCancel)
 {
  delete AverageFilterDialogForm;
  return;
 }

 int Value = atoi(AverageFilterDialogForm->Edit1->Text.c_str());

 delete AverageFilterDialogForm;

 Byte* PrevisionLine = NULL;
 Byte* CurrentLine = NULL;
 Byte* NextLine = NULL;
 int I = 0, J = 0;
 int Summ = 0;

 for (I = 0; I <= Image1->Picture->Bitmap->Height - 1; I++)
 {
  CurrentLine = (Byte*)Image1->Picture->Bitmap->ScanLine[I];
  for (J = 0; J <= Image1->Picture->Bitmap->Width - 1; J++)
  {
   Summ = 0;

   if (I > 0)
   {
    PrevisionLine = (Byte*)Image1->Picture->Bitmap->ScanLine[I - 1];
    if (J > 0)
    {
     Summ += PrevisionLine[J - 1];
    }

    Summ = Summ + PrevisionLine[J];

    if (J + 1 < Image1->Picture->Bitmap->Width)
    {
     Summ += PrevisionLine[J + 1];
    }
   }

   if (J > 0)
   {
    Summ += CurrentLine[J - 1];
   }

   Summ += CurrentLine[J];

   if (J + 1 < Image1->Picture->Bitmap->Width)
   {
    Summ += CurrentLine[J + 1];
   }

   if (I + 1 < Image1->Picture->Bitmap->Height)
   {
    NextLine = (Byte*)Image1->Picture->Bitmap->ScanLine[I + 1];
    if (J > 0)
    {
     Summ += NextLine[J - 1];
    }

    Summ += NextLine[J];

    if (J + 1 < Image1->Picture->Bitmap->Width)
    {
     Summ += NextLine[J + 1];
    }
   }

   if ((int)(Summ / 9) <= Value)
    CurrentLine[J] = (Byte) Summ / 9;
  }
 }
 Image1->Visible = false;
 Image1->Visible = true;
}
//--------------------------------------------------------------------------
-
// Расстояние между двумя точками
int Distance(TVertex& V1, TVertex& V2)
{
 int a = abs(V1.Y - V2.Y);
 int b = abs(V1.X - V2.X);
 return sqrt(a*a + b*b);
}

//--------------------------------------------------------------------------
-
void __fastcall TChildForm::OSR()
{
 // Пороговое расстояние для простроения упрощенной фигуры
 const int Treshold = 5;

 // Сюда сохраняется результат распознования
 AnsiString Result;

 // Отладочная форма с изображением для работы
 OSRForm = new TOSRForm(this);

 // Направления движения жука
 typedef enum {North, East, South, West} TDirectional;
 TDirectional Direct;

 // Координаты первой встречи с текущим объектом
 int X,Y;

 // Временно их используем для задания нового размера рабочего изображения
 X = OSRForm->Width - OSRForm->Image1->Width;
 Y = OSRForm->Height - OSRForm->Image1->Height;
 OSRForm->Image1->Picture->Bitmap->Assign(Image1->Picture->Bitmap);
 OSRForm->Width = OSRForm->Image1->Width + X;
 OSRForm->Height = OSRForm->Image1->Height + Y;
 OSRForm->Image1->Canvas->Rectangle(0, 0, OSRForm->Image1->Width - 1,
                                    OSRForm->Image1->Height - 1);

 Graphics::TBitmap* FromImage = Image1->Picture->Bitmap;
 Graphics::TBitmap* ToImage = OSRForm->Image1->Picture->Bitmap;

 // Текущие координаты маркера
 int cX,cY;

 // Максимальные координаты, которые занимает фигура
 int MaxX = 0;
 int MaxY = FromImage->Height;

 // От этой координаты начинается новое сканирование по Y
 int BeginY = 0;

 // Обрабатываемые линии
 Byte *Line, *ToLine;

 char Symb = 'А';

 // Текущий байт
 Byte B = 0;

 bool SkipMode = false;
 while (true)
 {
  // Список координат текущего объекта
  TShapeVector ShapeVector;
  // Временная структура координат точки
  TVertex Vertex;

  // Поиск любого объекта
  // Идем до тех пор, пока не встретим черную область
  for (X = MaxX; X < FromImage->Width; X++)
  {
   for (Y = BeginY; Y < MaxY; Y++)
   {
    Line = (Byte*)FromImage->ScanLine[Y];
    if (Line[X] < 255)
     goto FindedLabel;
   }

   if ((X + 1 == FromImage->Width) && (Y == FromImage->Height))
   {
    X++;
    goto FindedLabel;
   }

   // Если прошли до самого правого края, расширяем границы поиска до низа
   if (X + 1 == FromImage->Width)
   {
    X = 0;

    MaxX = 0;
    BeginY = MaxY;
    MaxY = FromImage->Height;
   }

  }
FindedLabel:

  // Если не нашли ни одного черного пиксела, то выходим из процедуры
  if ((X == FromImage->Width) && (Y == FromImage->Height))
   break;

  // Сначала задача найти максимальные границы обнаруженной фигуры,
  // чтобы потом от нее начинать строить скелет
  // Также ищем самую верхнюю точку фигуры, для начала построения
  int MinX = Image1->Picture->Width;  // Самая левая координата

  MaxX = 0;
  MaxY = 0;

  // Самая верхняя точка
  TVertex TopPoint;
  TopPoint.Y = Image1->Picture->Height;

  // Поворачиваем налево (новое направление - север)
  cX = X;
  cY = Y - 1;

  Direct = North;
  Line = (Byte*)FromImage->ScanLine[cY];

  // Пока не придем в исходную точку, выделяем контур объекта
  while ((cX != X) || (cY != Y))
  {
   // В зависимости от текущего направления движения жука
   switch (Direct)
   {
     // Север
    case North:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      Direct = West;
      cX--;
      // Может это самая левая координата?
      if (MinX > cX)
       MinX = cX;
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = East;
      cX++;
      if (MaxX < cX)
       MaxX = cX;

     }
    }
    break;

    // Восток
    case East:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      Direct = North;
      cY--;
      Line = (Byte*)FromImage->ScanLine[cY];
      // Может это самая верхняя точка?
      if (TopPoint.Y > cY)
      {
       TopPoint.Y = cY;
       TopPoint.X = cX;
      }
     }
     // Иначе поворачиваем "направо"
     else
     {

      Direct = South;
      cY++;
      Line = (Byte*)FromImage->ScanLine[cY];
      if (MaxY < cY)
       MaxY = cY;
     }
    }
    break;

    // Юг
    case South:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      Direct = East;
      cX++;
      if (MaxX < cX)
       MaxX = cX;
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = West;
      cX--;
      // Может это самая левая координата?
      if (MinX > cX)
       MinX = cX;
     }
    }
    break;

    // Запад
    case West:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      Direct = South;
      cY++;
      Line = (Byte*)FromImage->ScanLine[cY];
      if (MaxY < cY)
       MaxY = cY;
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = North;
      cY--;
      Line = (Byte*)FromImage->ScanLine[cY];
      // Может это самая верхняя точка?
      if (TopPoint.Y > cY)
      {
       TopPoint.Y = cY;
       TopPoint.X = cX;
      }
     }
    }
   }
  }

  TopPoint.X++;

  if ((!TopPoint.X) && (!TopPoint.Y))
  {
   TopPoint.X = X;
   TopPoint.Y = Y;
  }
  else
  {
   X = TopPoint.X;
   Y = TopPoint.Y;
  }

  // Постройка скелета
  ToLine = (Byte*)ToImage->ScanLine[Y];
  ToLine[X] = 0;

  // Поворачиваем налево (новое направление - юг)
  cX = X;
  cY = Y;

  Vertex.X = X;
  Vertex.Y = Y;
  ShapeVector.push_back(Vertex);

  Direct = East;
  Line = (Byte*)FromImage->ScanLine[cY];

  // Пока не придем в исходную точку, выделяем контур объекта
  do
  {
   // В зависимости от текущего направления движения жука
   switch (Direct)

   {
     // Север
    case North:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      ToLine = (Byte*)ToImage->ScanLine[cY];
      ToLine[cX] = 0;

      Vertex.X = cX;
      Vertex.Y = cY;
      if (Distance(Vertex, ShapeVector[ShapeVector.size() - 1]) >=
Treshold)
       ShapeVector.push_back(Vertex);

      Direct = West;
      cX--;
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = East;
      cX++;
     }
    }
    break;

    // Восток
    case East:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      ToLine = (Byte*)ToImage->ScanLine[cY];
      ToLine[cX] = 0;

      Vertex.X = cX;
      Vertex.Y = cY;
      if (Distance(Vertex, ShapeVector[ShapeVector.size() - 1]) >=
Treshold)
       ShapeVector.push_back(Vertex);

      Direct = North;
      cY--;
      Line = (Byte*)FromImage->ScanLine[cY];
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = South;
      cY++;
      Line = (Byte*)FromImage->ScanLine[cY];
     }
    }
    break;

    // Юг
    case South:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      ToLine = (Byte*)ToImage->ScanLine[cY];
      ToLine[cX] = 0;

      Vertex.X = cX;
      Vertex.Y = cY;
      if (Distance(Vertex, ShapeVector[ShapeVector.size() - 1]) >=
Treshold)
       ShapeVector.push_back(Vertex);

      Direct = East;
      cX++;
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = West;
      cX--;
     }
    }
    break;

    // Запад
    case West:
    {
     B = Line[cX];
     // Если элемент "черный", поворачиваем снова "налево"
     if (B < 255)
     {
      ToLine = (Byte*)ToImage->ScanLine[cY];
      ToLine[cX] = 0;

      Vertex.X = cX;
      Vertex.Y = cY;
      if (Distance(Vertex, ShapeVector[ShapeVector.size() - 1]) >=
Treshold)

       ShapeVector.push_back(Vertex);

      Direct = South;
      cY++;
      Line = (Byte*)FromImage->ScanLine[cY];
     }
     // Иначе поворачиваем "направо"
     else
     {
      Direct = North;
      cY--;
      Line = (Byte*)FromImage->ScanLine[cY];
     }
    }
   }
  } while ((cX != X) || (cY != Y));

  Vertex.X = X;
  Vertex.Y = Y;
  ShapeVector.push_back(Vertex);

  ToImage->Canvas->Pen->Color = clRed;
  ToImage->Canvas->MoveTo(ShapeVector[0].X, ShapeVector[0].Y);
  for (UINT i = 1; i < ShapeVector.size(); i++)
  {
   ToImage->Canvas->LineTo(ShapeVector[i].X, ShapeVector[i].Y);
  }

  for (UINT i = 0; i < ShapeVector.size(); i++)
  {
   ShapeVector[i].X -= MinX;
   ShapeVector[i].Y -= Y;
  }

/*

  if (Symb == 'Й')
  {
   Symb++;
  }

  if (Symb == 'а')
  {
   // Symb = 'A';
   break;
  }

  if ((Symb != 'Ы') && (!SkipMode))
  {
   AnsiString FileName = ExtractFilePath(Application->ExeName) + "TPL\\";
   FileName += Symb;

   ofstream OutFile(FileName.c_str());
   for (UINT i = 0; i < ShapeVector.size(); i++)
   {
    OutFile << IntToStr(ShapeVector[i].X).c_str() << endl;
    OutFile << IntToStr(ShapeVector[i].Y).c_str() << endl;
   }
   OutFile.close();

   Symb++;
  }
  else
  {
   if (SkipMode)
   {
    SkipMode = false;
    Symb++;
   }
   else if (Symb == 'Ы')
    SkipMode = true;
  }
*/

  TTemplate* Template = FindTemplate(ShapeVector);
  if (Template)
   Result += Template->Symb;
 }

 //OSRForm->Show();
 delete OSRForm;

 Memo1->Text = Result;
}
//--------------------------------------------------------------------------
-
TTemplate* FindTemplate(TShapeVector Vec)
{
 TTemplate Template;
 Template.Vec = Vec;
 for (UINT i = 0; i < Templates.size(); i++)
 {
  if (Templates[i] == Template)
   return &Templates[i];
 }

 return NULL;
}
//--------------------------------------------------------------------------
-


                             Снапшоты программы.


                                Начало работы

[pic]



                         Произведено распознавание.

[pic]