Белорусский Государственный Университет Информатики и
Радиоэлектроники.
Контрольная работа
по дисциплине
«МАГИ»
«Программа распознавания символов»
Выполнил студент группы
500501
Балахонов Е.В.
Требуется написать программу, способную
распознавать графически представленные символы в виде растрового изображения и
преобразовывать в обычный текст.
-
платформа: Win32,
-
формат графического изображения: Windows Bitmap (BMP),
8 бит,
-
шрифт для распознавания: Arial, 16
В качестве среды разработки будет использоваться Borland C++ Builder 5.
В качестве алгоритма выделения контуров будем
использовать алгоритм жука.
Отслеживающие
алгоритмы основаны на том, что
на изображении отыскивается объект (первая встретившаяся точка объекта) и
контур объекта отслеживается и векторизуется. Достоинством данных алгоритмов
является их простота, к недостаткам можно отнести их последовательную
реализацию и некоторую сложность при поиске и обработке внутренних контуров.
Пример отслеживающего алгоритма - "алгоритма жука" - приведен на рис.
5.12. Жук начинает движение с белой области по направлению к черной, Как только
он попадает на черный элемент, он поворачивает налево и переходит к следующему
элементу. Если этот элемент белый, то жук поворачивается направо, иначе -
налево. Процедура повторяется до тех пор, пока жук не вернется в исходную
точку. Координаты точек перехода с черного на белое и с белого на черное и
описывают границу объекта.
На рис. 1 показана схема
работы такого алгоритма.
Рис. 1. Схема работы
отслеживающего алгоритма «жука».
При нахождении новой точки контура,
рассчитывается расстояние между предыдущей найденной точкой и новой. Если оно
превышает некоторую границу (по умолчанию в 5 единиц), она запоминается. К
концу построения скелетной линии программа имеет массив координат вершин
ломаной, которая является скелетной линией объекта.
После построения скелетной линии производится
сравнение ее с списком шаблонов известных символов. При нахождении совпадения,
программа записывает в строку найденный символ.
//---------------------------------------------------------------------------
#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;
}
//---------------------------------------------------------------------------
Начало работы
Произведено
распознавание.
! |
Как писать рефераты Практические рекомендации по написанию студенческих рефератов. |
! | План реферата Краткий список разделов, отражающий структура и порядок работы над будующим рефератом. |
! | Введение реферата Вводная часть работы, в которой отражается цель и обозначается список задач. |
! | Заключение реферата В заключении подводятся итоги, описывается была ли достигнута поставленная цель, каковы результаты. |
! | Оформление рефератов Методические рекомендации по грамотному оформлению работы по ГОСТ. |
→ | Виды рефератов Какими бывают рефераты по своему назначению и структуре. |