Програма розпізнавання символів
Написати програму, здатну розпізнавати графічно представлені символи у вигляді растрового зображення і перетворювати на звичайний текст.
платформа: Win32,
формат графічного зображення: Windows Bitmap (BMP), 8 біт,
шрифт для розпізнавання: Arial, 16
Етап 1. Виділення контуру об'єкта, визначення його меж.
Як алгоритму виділення контурів будемо використовувати алгоритм жука.
Загальний опис алгоритму.
відстежують алгоритми грунтуються на тому, що на зображенні відшукується об'єкт (перша зустрілася точка об'єкта) і контур об'єкта відстежується і векторізуется. Перевагою даних алгоритмів є їх простота, до недоліків можна віднести їх послідовну реалізацію і деяку складність в процесі пошуку та обробки внутрішніх контурів. Приклад відслідковує алгоритму - "алгоритму жука" - наведено на рис. 5.12. Жук починає рух з білою області у напрямку до чорної, Як тільки він потрапляє на чорний елемент, він повертає ліворуч і переходить до наступного елементу. Якщо цей елемент білий, то жук повертається направо, інакше - ліворуч. Процедура повторюється до тих пір, поки жук не повернеться у вихідну точку. Координати точок переходу з чорного на біле і з білого на чорне і описують кордон об'єкта.
На рис. 1 показана схема роботи такого алгоритму.
Рис. 1. Схема роботи відслідковує алгоритму "жука".
Етап 2. Побудова на основі контуру об'єкта скелетної лінії.
При знаходженні нової точки контуру, розраховується відстань між попередньою знайденої точкою і нової. Якщо воно перевищує деяку кордон (за замовчуванням в 5 одиниць), вона запам'ятовується. До кінця побудови скелетної лінії програма має масив координат вершин ламаної, яка є скелетної лінією об'єкта.
Етап 3. Порівняння отриманої скелетної лінії з списком шаблонів.
Після побудови скелетної лінії проводиться порівняння її з списком шаблонів відомих символів. При знаходженні збіги, програма записує в рядок знайдений символ.
Оригінальний текст програми.
//--------------------------------------------- ------------------------------
# include
# pragma hdrstop
# include
# include
# 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:: Load9323_ (AnsiString FileName)
(
try
(
9323_1-> Picture-> LoadFromFile (FileName);
)
catch (EInvalidGraphic & Exception)
(
AnsiString Error = "Помилка завантаження файлу зображення! Помилка системи:";
Error + = Exception.Message;
MessageBox (this-> Handle, Error.c_str (), "Помилка", MB_OK | MB_ICONERROR);
return false;
)
if (9323_1-> 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 Picture-> Bitmap-> Height - 1; I + +)
(
CurrentLine = (Byte *) 9323_1-> Picture-> Bitmap-> ScanLine [I];
for (J = 0; J Picture-> Bitmap-> Width - 1; J + +)
(
Summ = 0;
if (I> 0)
(
PrevisionLine = (Byte *) 9323_1-> Picture-> Bitmap-> ScanLine [I - 1];
if (J> 0)
(
Summ + = PrevisionLine [J - 1];
)
Summ = Summ + PrevisionLine [J];
if (J + 1 <9323_1-> Picture-> Bitmap-> Width)
(
Summ + = PrevisionLine [J + 1];
)
)
if (J> 0)
(
Summ + = CurrentLine [J - 1];
)
Summ + = CurrentLine [J];
if (J + 1 <9323_1-> Picture-> Bitmap-> Width)
(
Summ + = CurrentLine [J + 1];
)
if (I + 1 <9323_1-> Picture-> Bitmap-> Height)
(
NextLine = (Byte *) 9323_1-> Picture-> Bitmap-> ScanLine [I + 1];
if (J> 0)
(
Summ + = NextLine [J - 1];
)
Summ + = NextLine [J];
if (J + 1 <9323_1-> Picture-> Bitmap-> Width)
(
Summ + = NextLine [J + 1];
)
)
if ((int) (Summ/9) Visible = false;
9323_1-> 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-> 9323_1-> Width;
Y = OSRForm-> Height - OSRForm-> 9323_1-> Height;
OSRForm-> 9323_1-> Picture-> Bitmap-> Assign (9323_1-> Picture-> Bitmap);
OSRForm-> Width = OSRForm-> 9323_1-> Width + X;
OSRForm-> Height = OSRForm-> 9323_1-> Height + Y;
OSRForm-> 9323_1-> Canvas-> Rectangle (0, 0, OSRForm-> 9323_1-> Width - 1,
OSRForm-> 9323_1-> Height - 1);
Graphics:: TBitmap * From9323_ = 9323_1-> Picture-> Bitmap;
Graphics:: TBitmap * To9323_ = OSRForm-> 9323_1-> Picture-> Bitmap;
// Поточні координати маркера
int cX, cY;
// Максимальні координати, які займає фігура
int MaxX = 0;
int MaxY = From9323_-> 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 Width; X + +)
(
for (Y = BeginY; Y (
Line = (Byte *) From9323_-> ScanLine [Y];
if (Line [X] <255)
goto FindedLabel;
)
if ((X + 1 == From9323_-> Width) & & (Y == From9323_-> Height))
(
X + +;
goto FindedLabel;
)
// Якщо пройшли до самого правого краю, розширюємо межі пошуку до низу
if (X + 1 == From9323_-> Width)
(
X = 0;
MaxX = 0;
BeginY = MaxY;
MaxY = From9323_-> Height;
)
)
FindedLabel:
// Якщо не знайшли жодного чорного піксела, то виходимо із процедури
if ((X == From9323_-> Width) & & (Y == From9323_-> Height))
break;
// Спочатку завдання знайти максимальні межі виявленої фігури,
// щоб потім від неї починати будувати скелет
// Також шукаємо найвищу точку фігури, для початку побудови
int MinX = 9323_1-> Picture-> Width;// Сама ліва координата
MaxX = 0;
MaxY = 0;
// Сама верхня точка
TVertex TopPoint;
TopPoint.Y = 9323_1-> Picture-> Height;
// Звертаємо ліворуч (новий напрямок - північ)
cX = X;
cY = Y - 1;
Direct = North;
Line = (Byte *) From9323_-> 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 MaxX = cX;
)
)
break;
// Схід
case East:
(
B = Line [cX];
// Якщо елемент "чорний", повертаємо знову "наліво"
if (B <255)
(
Direct = North;
cY -;
Line = (Byte *) From9323_-> ScanLine [cY];
// Може це сама верхня точка?
if (TopPoint.Y> cY)
(
TopPoint.Y = cY;
TopPoint.X = cX;
)
)
// Інакше повертаємо "направо"
else
(
Direct = South;
cY + +;
Line = (Byte *) From9323_-> ScanLine [cY];
if (MaxY MaxY = cY;
)
)
break;
// Південь
case South:
(
B = Line [cX];
// Якщо елемент "чорний", повертаємо знову "наліво"
if (B <255)
(
Direct = East;
cX + +;
if (MaxX 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 *) From9323_-> ScanLine [cY];
if (MaxY MaxY = cY;
)
// Інакше повертаємо "направо"
else
(
Direct = North;
cY -;
Line = (Byte *) From9323_-> 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 *) To9323_-> ScanLine [Y];
ToLine [X] = 0;
// Звертаємо ліворуч (новий напрямок - південь)
cX = X;
cY = Y;
Vertex.X = X;
Vertex.Y = Y;
ShapeVector.push_back (Vertex);
Direct = East;
Line = (Byte *) From9323_-> ScanLine [cY];
// Ще не прийдемо у вихідну точку, виділяємо контур об'єкта
do
(
// В залежності від поточного напрямку руху жука
switch (Direct)
(
// Північ
case North:
(
B = Line [cX];
// Якщо елемент "чорний", повертаємо знову "наліво"
if (B <255)
(
ToLine = (Byte *) To9323_-> 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 *) To9323_-> 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 *) From9323_-> ScanLine [cY];
)
// Інакше повертаємо "направо"
else
(
Direct = South;
cY + +;
Line = (Byte *) From9323_-> ScanLine [cY];
)
)
break;
// Південь
case South:
(
B = Line [cX];
// Якщо елемент "чорний", повертаємо знову "наліво"
if (B <255)
(
ToLine = (Byte *) To9323_-> 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 *) To9323_-> 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 *) From9323_-> ScanLine [cY];
)
// Інакше повертаємо "направо"
else
(
Direct = North;
cY -;
Line = (Byte *) From9323_-> ScanLine [cY];
)
)
)
) while ((cX! = X) | | (cY! = Y));
Vertex.X = X;
Vertex.Y = Y;
ShapeVector.push_back (Vertex);
To9323_-> Canvas-> Pen-> Color = clRed;
To9323_-> Canvas-> MoveTo (ShapeVector [0]. X, ShapeVector [0]. Y);
for (UINT i = 1; i (
To9323_-> Canvas-> LineTo (ShapeVector [i]. X, ShapeVector [i]. Y);
)
for (UINT i = 0; 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 (
OutFile