1.         Microsoft Developer Network (MSDN) [Электрон. ресурс]. ‑ Режим доступа: http://msdn.ru/

2.         Ковалева И.Л., "Алгоритмы обработки изображений", БНТУ, 2007


ПРИЛОЖЕНИЕ A

Небольшой мануал по проге:

- Текст должен быть черным по белому

- Картинки для теста есть в архиве.

- Ориентация текста не под углом.

- Размер картинки желательно не меньше чем, те, что лежат в архиве, потому что при маленьком изображении плохо распознается из-за сливания пикселей.

- Распознавание персептроном с несколькими сумматорами и алгоритмом обучения без учета правильности ответа (она может это спросить=)

- Для обучения персептрона надо открыть изображение "Картинка для обучения.png" из папки "тестовые изображения" или создать аналогичную самостоятельно и открыть ее. Потом нажать "сегментация", Потом "Обучить", можно сохранить обучение, нажав "сохранить". Теперь можно открывать изображение, которое будет распознаваться. Для распознавания надо нажать "сегментация", потом "распознать".

- Если проводилось сохранение обучения, то можно не обучать. Для распознавания в таком случае надо делать следующие: открываешь распознаваемое изображение, нажимаешь "Сегментация", нажимаешь "загрузить", нажимаешь "распознать".

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

- путь к EXE-шнику: \WordSearcher\WordSearcher\bin\Debug\ WordSearcher.exe

Если будут какие-то баги или вопросы, сообщай - исправлю.

С уважением, Свирко Юрий

Mail: sv1r4.sd@gmail.com

Phone: 8-033-63-123-60

ЛИСТИНГ ПРОГРАММЫ

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

namespace WordSearcher

{

public partial class FormMain : Form

{

/// <summary>

/// Флаг разрешения распознавания

/// </summary>

bool enableRecognize = false;

/// <summary>

/// Размер битмапа со словом "Указ"

/// к этому размеру подгоняются все отсалдьные распознаваемые битмапы

/// </summary>

private static Size imSize = new Size(65, 25);

/// <summary>

/// Состояние формы

/// </summary>

private FormState formState = FormState.Empty;

/// <summary>

/// Масив битмапов сос словами текста

/// </summary>

private List<Bitmap> words;

/// <summary>

/// Объект класса для распознавания(персептрон)

/// </summary>

private Recognizer r = new Recognizer(imSize, 750, 2);

/// <summary>

/// Метод для делания активными неактивными кнопок управления

/// в зависимости от этапа обработк изображения

/// </summary>

/// <param name="fs">текущее состояние</param>

private void ButtonsEnabled(FormState fs)

{

formState = fs;

switch (fs)

{

case FormState.Empty:

buttonOpen.Enabled = true;

buttonSegment.Enabled = false;

buttonRecognize.Enabled = false;

buttonTeach.Enabled = false;

buttonLoadTeaching.Enabled = false;

buttonSaveTeaching.Enabled = false;

pictureBoxMain.Image = null;

dataGridViewSegments.Rows.Clear();

break;

case FormState.Open:

buttonOpen.Enabled = true;

buttonSegment.Enabled = true;

buttonRecognize.Enabled = false;

buttonTeach.Enabled = false;

buttonLoadTeaching.Enabled = false;

buttonSaveTeaching.Enabled = false;

dataGridViewSegments.Rows.Clear();

break;

case FormState.Segmented:

buttonOpen.Enabled = true;

buttonSegment.Enabled = true;

if (enableRecognize)

buttonRecognize.Enabled = true;

else

buttonRecognize.Enabled = false;

buttonTeach.Enabled = true;

buttonLoadTeaching.Enabled = true;

buttonSaveTeaching.Enabled = false;

break;

case FormState.Teached:

buttonOpen.Enabled = true;

buttonSegment.Enabled = false;

buttonRecognize.Enabled = true;

buttonTeach.Enabled = false;

buttonLoadTeaching.Enabled = false;

buttonSaveTeaching.Enabled = true;

enableRecognize = true;

break;

case FormState.Deserialized:

buttonOpen.Enabled = true;

buttonSegment.Enabled = false;

buttonRecognize.Enabled = true;

buttonTeach.Enabled = false;

buttonLoadTeaching.Enabled = false;

buttonSaveTeaching.Enabled = true;

enableRecognize = true;

break;

case FormState.Recognized:

buttonOpen.Enabled = true;

buttonSegment.Enabled = false;

buttonRecognize.Enabled = true;

buttonTeach.Enabled = false;

buttonLoadTeaching.Enabled = true;

buttonSaveTeaching.Enabled = true;

break;

}

}

public FormMain()

{

InitializeComponent();

}

private void buttonOpen_Click(object sender, EventArgs e)

{

try

{

Bitmap b;

if (openFileDialog1.ShowDialog() == DialogResult.OK)

{

//Если изобраение имеет индексированный формат

//то переводим его в обычный, потомутчо с индексировнным не работат

//setpixel

b = new Bitmap(openFileDialog1.FileName);

if (b.PixelFormat == System.Drawing.Imaging.PixelFormat.Format1bppIndexed ||

b.PixelFormat == System.Drawing.Imaging.PixelFormat.Format4bppIndexed ||

b.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)

b = new Bitmap(b);

pictureBoxMain.Image = b;

this.ButtonsEnabled(FormState.Open);

}

else

{

this.ButtonsEnabled(FormState.Empty);

}

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

}

private void buttonSegment_Click(object sender, EventArgs e)

{

//Получаем набор битмапов соответствующих словам текста

words = Segmentation.GetWords((Bitmap)pictureBoxMain.Image);

dataGridViewSegments.RowCount = words.Count;

int i =0;

//Перебираем слов и отображаем в таблице

foreach (Bitmap word in words)

{

dataGridViewSegments.Rows[i].Cells[0].Value = word;

i++;

}

ButtonsEnabled(FormState.Segmented);

}

private void buttonRecognize_Click(object sender, EventArgs e)

{

int i = 0;

foreach (Bitmap word in words)

{

dataGridViewSegments.Rows[i].Cells[1].Value = r.Recognize(Recognizer.NormalizeBitmap(word,imSize));

i++;

}

ButtonsEnabled(FormState.Recognized);

}

private void buttonTeach_Click(object sender, EventArgs e)

{

//Перебираем слова и обучаем ими

//т.к. класса для распознаваня два то

//первый класс обучаем просто изображение слова

//а второй обучаем противопорложным изображение(т.е инвертируем цвета исходного изображения)

for (int i = 0; i < 5; i++)

{

foreach (Bitmap word in words)

{

r.Teach(Recognizer.NormalizeBitmap(word, imSize), 0);

r.Teach(Recognizer.InverseBitmap(Recognizer.NormalizeBitmap(word, imSize)), 1);

}

}

ButtonsEnabled(FormState.Teached);

}

private void buttonSaveTeaching_Click(object sender, EventArgs e)

{

r.SerializeParams();

ButtonsEnabled(FormState.Serialized);

}

private void buttonLoadTeaching_Click(object sender, EventArgs e)

{

r.DeserializeParams();

ButtonsEnabled(FormState.Deserialized);

}

private void FormMain_Load(object sender, EventArgs e)

{

this.ButtonsEnabled(FormState.Empty);

}

}

// <summary>

/// Состояния изображения

/// </summary>

enum FormState

{

/// <summary>

/// изображение не открыто

/// </summary>

Empty,

/// <summary>

/// Изображение открыто

/// </summary>

Open,

/// <summary>

/// Сегментировано

/// </summary>

Segmented,

/// <summary>

/// Персептрон обучен

/// </summary>

Teached,

/// <summary>

/// Параметры персептрона сохранены

/// </summary>

Serialized,

/// <summary>

/// Параметры персептрона загружены

/// </summary>

Deserialized,

/// <summary>

/// Распознано

/// </summary>

Recognized

}

}

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Drawing;

using System.Windows.Forms;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

namespace WordSearcher

{

/// <summary>

/// Реализует распозноание изображений

/// на базе персептрона

/// </summary>

class Recognizer

{

/// <summary>

/// матрица знаков входов персептрона

/// </summary>

private int[,] xa;

/// <summary>

/// Массив лямд

/// </summary>

private int[,] l;

/// <summary>

/// Массив имен классов

/// </summary>

private string[] classes = {"Указ",

"Не указ"};

/// <summary>

/// Массив имен классов

/// </summary>

public string[] ClassesList

{

get { return classes; }

}

/// <summary>

/// Инициализирует xa-матрицу

/// </summary>

/// <param name="sz">размер изображения</param>

/// <param name="aCount">количесвто а-элементов</param>

/// <param name="lCount">количесвто классов</param>

public Recognizer(Size sz, int aCount, int lCount)

{

Random r = new Random();

//Создание матрцы ха

xa = new int[sz.Height * sz.Width, aCount];

//Создание матрицы лямд

l = new int[lCount,aCount];

//Первоначальная

//иницализация лямд еденицами

for (int i = 0; i < l.GetLength(0); i++)

{

for (int j = 0; j < l.GetLength(1); j++)

{

l[i, j] = 1;

}

}

//заполнение матрицы

//для каждого рецептора(строчки)

//назначаетя только один а-элемент(столбец) со знаком + или -

for (int i = 0; i < xa.GetLength(0); i++)

{

xa[i, r.Next(aCount)] = (int)Math.Pow(-1, r.Next(1, 3));

}

}

/// <summary>

/// Обучение персептрона

/// </summary>

/// <param name="b">битмап для обучения</param>

/// <param name="classindex">имя класса к ккоторому относиться изображение</param>

public void Teach(Bitmap b, int classindex)

{

int[] x = new int[b.Height * b.Width];

int k = 0;

//Инициализация входных рецепторов

for (int i = 0; i < b.Width; i++)

{

for (int j = 0; j < b.Height; j++)

{

if (b.GetPixel(i, j) == Color.FromArgb(0, 0, 0))

x[k] = 1;

k++;

}

}

//Вектор сумм рецепторов

int[] sumx = new int[xa.GetLength(1)];

//Вектор выходов А-элементов

int[] outa = new int[xa.GetLength(1)];

//суммирование сигналов от рецепторов

for (int i = 0; i < xa.GetLength(1); i++)

{

for (int j = 0; j < xa.GetLength(0); j++)

{

sumx[i] += x[j] * xa[j, i];

}

//Если сумма больше нуля выход а элемента 1

if (sumx[i] > 0)

outa[i] = 1;

}

//изменение коэфициетов лямда

for (int i = 0; i < outa.Length; i++)

{

//Если а-элемент возбужден то изменяем лямды

if (outa[i] == 1)

{

//перебор всех классов

for (int j = 0; j < l.GetLength(0); j++)

{

//Увеличение на 1 лямд для класса который обучается

//и уменьшение для всех осатльных

if (classindex == j)

l[j, i]++;

else

l[j, i]--;

}

}

}

}

/// <summary>

/// Распознавание изобржения

/// </summary>

/// <param name="b">битмап изображения</param>

/// <returns>имя класса к которому отнесено изображение</returns>

public string Recognize(Bitmap b)

{

int[] x = new int[b.Height * b.Width];

int k = 0;

//Инициализация входных рецепторов

for (int i = 0; i < b.Width; i++)

{

for (int j = 0; j < b.Height; j++)

{

if (b.GetPixel(i, j) == Color.FromArgb(0, 0, 0))

x[k] = 1;

k++;

}

}

//Вектор суммрецепторов

int[] sumx = new int[xa.GetLength(1)];

//Вектор выходов А-элементов

int[] outa = new int[xa.GetLength(1)];

//суммирование сигналов от рецепторов

for (int i = 0; i < xa.GetLength(1); i++)

{

for (int j = 0; j < xa.GetLength(0); j++)

{

sumx[i] += x[j] * xa[j, i];

}

//Если сумма больше нуля выход а элемента 1

if (sumx[i] > 0)

outa[i] = 1;

}

//Создание масива значений сумматоров

//каждый для отдельного класса

int[] sum = new int[l.GetLength(0)];

//Нахождение значений сумматоров для каждого класса

for (int i = 0; i < sum.Length; i++)

{

for (int j = 0; j < xa.GetLength(1); j++)

{

sum[i] += outa[j] * l[i, j];

}

}

//нахождение максимального значения сумматор

//именно оно соответствует распознанному классу

int max = sum[0];

int maxindex = 0;

for (int i = 1; i < sum.Length; i++)

{

if (max < sum[i])

{

max = sum[i];

maxindex = i;

}

}

//Возвращается имя класса с максимальным значением сумматора

return classes[maxindex];

}

/// <summary>

/// Сериализация массива лямд(сохранение в файл) для сохранения обученяи персептрона

/// </summary>

public void SerializeParams()

{

try

{

BinaryFormatter bf = new BinaryFormatter();

FileStream fs = new FileStream("l.dat", FileMode.Create);

bf.Serialize(fs, l);

fs.Close();

bf = new BinaryFormatter();

fs = new FileStream("xa.dat", FileMode.Create);

bf.Serialize(fs, xa);

fs.Close();

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

}

/// <summary>

/// Десериализация массива лямд(чтение из файла)

/// </summary>

public void DeserializeParams()

{

try

{

BinaryFormatter bf = new BinaryFormatter();

FileStream fs = new FileStream("l.dat", FileMode.Open);

l = (int[,])bf.Deserialize(fs);

fs.Close();

bf = new BinaryFormatter();

fs = new FileStream("xa.dat", FileMode.Open);

xa = (int[,])bf.Deserialize(fs);

fs.Close();

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

}

/// <summary>

/// Подгонка битмапа по размеру и его бинаризация

/// </summary>

/// <param name="b">входной битмап</param>

/// <param name="sz">новый размер битмапа</param>

/// <returns>нормализованный битмап</returns>

public static Bitmap NormalizeBitmap(Bitmap b, Size sz)

{

//Подгонка размера

Bitmap inImg = new Bitmap(b, sz);

//Создание выходного битмапа на основе подогнанного

Bitmap outImg = new Bitmap(inImg);

//находим среднее значение яркости

int sum = 0;

for (int i = 0; i < outImg.Width; i++)

{

for (int j = 0; j < outImg.Height; j++)

{

Color cl = ((Bitmap)inImg).GetPixel(i,j);

sum += (cl.R + cl.G + cl.B) / 3;

}

}

int sredn = sum / (inImg.Width * inImg.Height);

//Просматриваем изображнеи и бинаризуем его

for (int i = 0; i < outImg.Width; i++)

{

for (int j = 0; j < outImg.Height; j++)

{

Color cl = ((Bitmap)inImg).GetPixel(i,j);

int gray = (cl.R + cl.G + cl.B) / 3;

if (gray > sredn)

outImg.SetPixel(i, j, Color.FromArgb(255, 255, 255));

else

outImg.SetPixel(i, j, Color.FromArgb(0, 0, 0));

}

}

return outImg;

}

/// <summary>

/// Инверсия цвета битмапа

/// </summary>

/// <param name="b"></param>

/// <returns></returns>

public static Bitmap InverseBitmap(Bitmap b)

{

Bitmap outImg = new Bitmap(b.Width, b.Height);

for (int i = 0; i < b.Width; i++)

{

for (int j = 0; j < b.Height; j++)

{

Color c = b.GetPixel(i,j);

outImg.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));

}

}

return outImg;

}

}

}

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Drawing;

namespace WordSearcher

{

class Segmentation

{

/// <summary>

/// Разбиение битмапа с текстоми на строки

/// </summary>

/// <param name="b">исходный битмап</param>

/// <returns>коллекция строк</returns>

public static List<Bitmap> GetStrings(Bitmap text)

{

List<Bitmap> strs = new List<Bitmap>();

List<int> whiteLineIndexes = new List<int>();

//Находим все белые горзонатльные линии на ихображении

//и запоминаем их индексы

for (int j = 0; j < text.Height; j++)

{

bool whiteLineFound = true;

for (int i = 0; i < text.Width; i++)

{

if (text.GetPixel(i, j) != Color.FromArgb(255, 255, 255))

{

whiteLineFound = false;

break;

}

}

if (whiteLineFound)

whiteLineIndexes.Add(j);

}

//Выделение строк между белыми несоседними линиями

for (int i = 0; i < whiteLineIndexes.Count-1; i++)

{

if (whiteLineIndexes[i + 1] - whiteLineIndexes[i] > 4)

{

strs.Add(text.Clone(

new Rectangle(

0,

whiteLineIndexes[i],

text.Width,

whiteLineIndexes[i + 1] - whiteLineIndexes[i]+1),

System.Drawing.Imaging.PixelFormat.Format24bppRgb));

}

}

return strs;

}

/// <summary>

/// Получить список слов отдельной строки

/// </summary>

/// <param name="str">битмап со строкой текста</param>

/// <returns>спсиок слов строки</returns>

public static List<Bitmap> GetStringWords(Bitmap str)

{

List<Bitmap> words = new List<Bitmap>();

List<int> whiteLineIndexes = new List<int>();

//Находим все белые вертикальные линии на изображении

//и запоминаем их индексы

for (int i = 0; i < str.Width; i++)

{

bool whiteLineFound = true;

for (int j = 0; j < str.Height; j++)

{

if (str.GetPixel(i, j).R < 100)

{

whiteLineFound = false;

break;

}

}

if (whiteLineFound)

whiteLineIndexes.Add(i);

}

//Ширина пробела

int spaceWidth = 0;

int sum = 0;

int n = 0;

//Вычисление ширины пробела

for (int i = 0; i < whiteLineIndexes.Count - 1; i++)

{

int d = whiteLineIndexes[i + 1] - whiteLineIndexes[i];

if (d > 1)

{

sum += d;

n++;

}

}

//Ширина пробела необходимо при дальнейшем выделении слов

//коэф. подобран вручную

spaceWidth = (int)Math.Round(sum * 0.45 / n + 0.1);

//начальная координата слова

int wordBegin = 0;

//конечная координат слова

int wordEnd = 0;

//флаг указывающий на то найденно ли начало слова или нет

//перволдится обратно в фолс после нахождения конца слова

bool wordFound = false;

//Счетчик ширины белой полоски

int whiteWidth = 0;

//Выделение слов

for (int i = 0; i < whiteLineIndexes.Count - 1; i++)

{

//если линии не соседние и флаг wordFound фолс т.е.

//слово еще не найдено

//запоминаем координату певрой линии это будет

//координатой началом слова

if ((whiteLineIndexes[i + 1] - whiteLineIndexes[i] > 1) &&

!wordFound)

{

//обнуление счетчика идущих подряд белыхз линий

whiteWidth = 0;

//флаг найденного слова в тру

wordFound = true;

//инициализируем начальную координату слова

wordBegin = whiteLineIndexes[i];

}

//инициализируем конечную координату слова

//если найдены не сосдение линии

//но не обрезаем битмап и не добавлям его в коллекцию

//т.к. необходисмо зделать проверку на ширину пробела

if ((whiteLineIndexes[i + 1] - whiteLineIndexes[i] > 1) &&

wordFound)

{

whiteWidth = 0;

wordEnd = whiteLineIndexes[i + 1];

}

//Если найденны соседние белые линии

//инкремируем счетчик белых линий и сравниваем ширину идущих подрд белых линий

//с ранее высчитаной средней шириной пробела

if (whiteLineIndexes[i + 1] - whiteLineIndexes[i] == 1)

{

whiteWidth++;

if ((whiteWidth >= spaceWidth) &&

(wordEnd - wordBegin > 1))

{

//Обрезаем и добавляем слово в коллекцию

words.Add(TrimBitmap(

str.Clone(

new Rectangle(

wordBegin,

0,

wordEnd - wordBegin + 1,

str.Height),

System.Drawing.Imaging.PixelFormat.Format24bppRgb)

)

);

//обнуляем счетчики

//и флаги

whiteWidth = 0;

wordFound = false;

wordBegin = 0;

wordEnd = 0;

}

}

}

return words;

}

/// <summary>

/// Получить битмапы всех слов в тексте

/// </summary>

/// <param name="text">битмап с текстом</param>

/// <returns>коллекция всех слов в тексте</returns>

public static List<Bitmap> GetWords(Bitmap text)

{

List<Bitmap> strs = GetStrings(text);

List<Bitmap> words = new List<Bitmap>();

foreach (Bitmap str in strs)

{

foreach (Bitmap word in GetStringWords(str))

{

words.Add(word);

}

}

return words;

}

/// <summary>

/// Обрезка белых полей вокруг изображения на битмапе

/// </summary>

/// <param name="bmp"></param>

/// <returns></returns>

public static Bitmap TrimBitmap(Bitmap bmp)

{

int left = 0;

int right = 0;

int top = 0;

int bottom = 0;

bool go = true;

//проход сверху

for (int j = 0; (j < bmp.Height) && go; j++)

{

for (int i = 0; (i < bmp.Width) && go; i++)

{

if (bmp.GetPixel(i, j) != Color.FromArgb(255, 255, 255))

{

go = false;

top = j;

}

}

}

go = true;

//проход снизу

for (int j = bmp.Height - 1; (j >= 0) && go; j--)

{

for (int i = 0; (i < bmp.Width) && go; i++)

{

if (bmp.GetPixel(i, j) != Color.FromArgb(255, 255, 255))

{

go = false;

bottom = j;

}

}

}

go = true;

//проход слева

for (int i = 0; (i < bmp.Width) && go; i++)

{

for (int j = 0; (j < bmp.Height) && go; j++)

{

if (bmp.GetPixel(i, j) != Color.FromArgb(255, 255, 255))

{

go = false;

left = i;

}

}

}

go = true;

//проход спарва

for (int i = bmp.Width - 1; (i >= 0) && go; i--)

{

for (int j = 0; (j < bmp.Height) && go; j++)

{

if (bmp.GetPixel(i, j) != Color.FromArgb(255, 255, 255))

{

go = false;

right = i;

}

}

}

return bmp.Clone(new Rectangle(left, top, right - left + 1, bottom - top + 1), System.Drawing.Imaging.PixelFormat.Format24bppRgb);

}

}

}


ОПИСЬ ЛИСТОВ ГРАФИЧЕСКОЙ ЧАСТИ

Лист 1 – Схема приложения

Функциональная схема приложения


Лист 2 – Диаграмма классов


Лист 3 – Результаты работы программы.


Лист 4 – Схема алгоритма сегментации текста.


Информация о работе «Распознавание графических символов»
Раздел: Информатика, программирование
Количество знаков с пробелами: 32565
Количество таблиц: 0
Количество изображений: 15

Похожие работы

Скачать
40216
13
3

... 0.98; gotoxy(10,24); writeln('Press any key to continue...'); readkey; graphiki; readkey; closegraph; End. ПРИЛОЖЕНИЕ Б Текст программы распознавания символов Program Final_of_work; uses graph; const BiH=50; {-------высота картинки в пикселях------} BiW=160; {-------ширина картинки в ...

Скачать
27902
0
3

... Правильное понимание таблицы читателем невозможно без учета информации о взаимном расположении строк, колонок и ячеек таблицы. Поэтому при автоматизированном распознавании табличных форм необходимо в выходном документе сохранить то же взаимное расположение этих структурных табличных элементов, что и в исходной таблице. Строки и колонки таблиц могут иметь иерархическую структуру (рис. 3), причем ...

Скачать
59645
0
0

... случае присоединения к компьютерной системе сканера, в окне папки «Панель управления» появляется соответствующий значок, позволяющий производить настройку. Таким образом, в большинстве программ работа со сканером производится при посредстве специального диалогового окна, обеспечивающего непосредственное взаимодействие со сканером. После того как пользователь дает команду на сканирование документа ...

Скачать
232852
0
0

... с приглашением по запросу (в машинной графике)required parameter обязательный параметрrequired space обязательный пробел (в системах подготовки текстов)requirements specification 1. техническое задание 2. описание требований к программному средствуrerun перезапуск, повторный запускreschedule переупорядочивать очередь (о диспетчере операционной системы)reschedule interval период переупорядочения ...

0 комментариев


Наверх