Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Применение ИНС для Ascii Art.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
6.18 Mб
Скачать

4 Тестирование

Для тестирования сконфигурируем приложение следующим образом:

Использовать кодирование цвета – Да

Масштабировать изображение – Да, на величину 600

Дублировать символы – Да

Использовать предобработку – Да

Символ закрашивания – ‘#’

Пустой символ – ‘ ‘

Начнем с самого простого случая – цветное изображение на белом фоне:

Изображение

Результат

На данном типе образов система работает корректно. Потеря информации почти не отсутствует.

Далее подадим изображение с фоном, который достаточно сильно отличается от целевого объекта

Изображение

Результат

Часть изображения, связанная с фоном теряется. Также теряются фрагменты, если они прилегают близко к фону. Это обусловлено в первую очередь недостатками алгоритма удаления цвета. Объект, который находиться в фокусе по-прежнему можно однозначно узнать.

Далее подадим изображения, относящиеся к природе. На данных примерах фон сливается с объектом, что максимально усложняет процесс распознавания.

Изображение

Результат

Система плохо кодирует такого рода изображения. Это связано, во-первых, с значительной потерей информации при удалении цвета, а во-вторых в том, что присутствует много объектов, и на общей картине и с большой вероятностью в один фрагмент попадают части разных, возможно даже не связанных объектов. В таком случае классифицировать что на изображении нам помогает по большому счету цвет.

Под конец приведем ещё несколько интересных частных случаев.

Рисунок 4.1: Входное изображение черно-белое

Рисунок 4.2: Входное изображение – Фото

Рисунок 4.3: Темное изображение на темном фоне

Рисунок 4.4: Светлое изображение на светлом фоне.

Рисунок 4.5: Изображения, содержащие большой объем информации.

5.Подведение итогов

В результате выполнения курсового была разработана и реализована библиотека для конструирования нейронных сетей. Особое внимание уделялось архитектуре, чтобы в итоге можно было собирать типовые нейронные сети, а при необходимости доопределять нужный функционал и интегрировать его с уже существующим. Но пришлось жертвовать быстродействием: так как повсеместно был использован динамический полиморфизм и наследование, код потерял в быстродействии и стал более раздутым, чем не расширяемые аналоги.

Также была разработана и реализована система для кодирования изображений в ascii art. Как было показано выше, система работает хорошо на тех изображениях, на которых, хорошо работает алгоритм удаления цвета. Если большая часть информации теряется после применения алгоритма удаления цвета, то и кодирование будет не качественным. Если изображение содержит большое количество объектов кодирование также работает не лучшим образом. В остальных же случаях был получен приемлемый результат: результат кодирования внешне похож на поданное изображение.

Применение ИНС в данной работе уменьшило недостатки описанных в начале работы методов перевода изображения в ascii art. Благодаря применения ИНС результат можно хранить в текстовых форматах, не поддерживающих цвет, передавать в компактном виде по сети, выводить на консоль, использовать как замены художника в советующем направлении искусства.

Стоит отметить что ручная работа, очевидно, качественнее результата работы системы. Однако ручная работа требует высоких затрат труда и времени, в то время как система кодирует очень быстро. Можно совместить работу программы и человека, что ускорит процесс выполнения работы художника.

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

Используемые источники:

1) Головко В.А. Нейронные сети: обучение, организация и применение

2) Павел Нестеров. Сборник статей про нейронные сети на habrahabr.

3) Муравьев Г.Л. Моделирование систем. Курс лекций. – БрГТУ, Брест, 2003.

Приложение А

[DataContract]

public class SimpleBackPropogation : DifferintiableLearningConfig, ILearn<double>

{

public SimpleBackPropogation(DifferintiableLearningConfig config)

: base(config)

{

}

/// <summary>

/// Обучения для отображения входных сигналов в выходные

/// </summary>

/// <param name="network"></param>

/// <param name="data"></param>

/// <param name="check"></param>

public void Train(INeuralNetwork<double> network, IList<NeuroImage<double>> data, IList<NeuroImage<double>> check)

{

//получаем удобную ссылку на сеть

MultiLayer net = network.Layer as MultiLayer;

this.CurrentEpoch = 0;

this.Succes = false;

double lastError = 0;

double[][] y = new double[data.Count][];

do

{

lastError = CurrentError;

//Перемешивание образов из выборки для данной эпохи

if (UseRandomShuffle == true)

{

RandomShuffle(ref data);

}

for (int curr = 0; curr < data.Count; curr++)

{

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

y[curr] = network.Compute(data[curr].Input);

//делаем оценку для текущего образа, и если погрешность допустима,идем

//на следующий образ. Данная модификация ускоряет процесс обучения и

//уменьшает среднеквадратичную ошибку

if (Compare(data[curr].Output, network.Layer[net.Layer.Length - 1].LastPulse) == true)

{

continue;

}

//считаем ошибку выходного слоя

OutputLayerError(data, net, curr);

//считаем ошибки скрытых слоев

HiddenlayerError(net);

//модифицируем веса и пороги

ModifyParametrs(data, net, curr);

}

//расчитываем ошибку

ComputeError(data, network,y);

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

ComputeRegularizationError(data, net);

System.Console.WriteLine("Eposh #" + CurrentEpoch.ToString() +

" finished; current error is " + CurrentError.ToString()

);

CurrentEpoch++;

}

while (CurrentEpoch < MaxEpoch && CurrentError > MinError &&

Math.Abs(CurrentError - lastError) > MinChangeError);

ComputeRecognizeError(check, network);

}

private void ComputeRecognizeError(IList<NeuroImage<double>> check, INeuralNetwork<double> network)

{

int accept = 0;

double LastError = 0;

RecogniseError = 0;

for (int i = 0; i < check.Count; i++)

{

double[] realOutput = network.Compute(check[i].Input);

double[] result = new double[realOutput.Length];

LastError = ErrorFunction.Compute(check[i].Output, realOutput);

RecogniseError += LastError;

double max = realOutput.Max();

int index = realOutput.ToList().IndexOf(max);

result[index] = 1;

if (ArrayCompare(result, check[i].Output) == true)

{

accept++;

}

}

RecognisePercent = (double)accept / (double)check.Count;

RecogniseError /= 2;

}

public static bool ArrayCompare(double[] a, double[] b)

{

if (a.Length == b.Length)

{

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

{

if (a[i] != b[i]) { return false; };

}

return true;

}

return false;

}

/// <summary>

/// Учет регуляризации для расчета среднеквадратичной ошибки

/// </summary>

/// <param name="data">обучающая выборка</param>

/// <param name="net">сеть</param>

private void ComputeRegularizationError(IList<NeuroImage<double>> data, MultiLayer net)

{

if (Math.Abs(Regularization - 0d) > Double.Epsilon)

{

double reg = 0;

for (int layerIndex = 0; layerIndex < net.Layer.Length; layerIndex++)

{

for (int neuronIndex = 0; neuronIndex < net.Layer[layerIndex].Neuron.Length; neuronIndex++)

{

for (int weightIndex = 0; weightIndex < net.Layer[layerIndex].Neuron[neuronIndex].Weights.Length; weightIndex++)

{

reg += net.Layer[layerIndex].Neuron[neuronIndex].Weights[weightIndex] *

net.Layer[layerIndex].Neuron[neuronIndex].Weights[weightIndex];

}

}

}

CurrentError += Regularization * reg / (2 * data.Count);

}

}

/// <summary>

/// Расчет ошибки сети

/// </summary>

/// <param name="data">обучающая выборка</param>

/// <param name="network">сеть</param>

private void ComputeError(IList<NeuroImage<double>> data, INeuralNetwork<double> network, double[][] realOutput)

{

int accept = 0;

double LastError = 0;

CurrentError = 0;

for (int i = 0; i < data.Count; i++)

{

LastError = ErrorFunction.Compute(data[i].Output, realOutput[i]);

CurrentError += LastError;

}

TeachPercent = (double)accept / (double)data.Count;

CurrentError /= 2;

}

/// <summary>

/// Модификация настраиваемых параметров сети

/// </summary>

/// <param name="data">обучающая выборка</param>

/// <param name="net"><сеть/param>

/// <param name="curr">номер текущего образа</param>

private void ModifyParametrs(IList<NeuroImage<double>> data, MultiLayer net, int curr)

{

for (int i = net.Layer.Length - 1; i >= 0; i--)//по всем слоям в обратном порядке

{

for (int j = 0; j < net[i].Neuron.Length; j++)//для каждого нейрона

{

double temp = Step * net[i].Neuron[j].CurrentError

* net[i].Neuron[j].ActivationFunction.Derivative(net[i].Neuron[j].LastPulse);

net[i].Neuron[j].Offset += temp;

for (int k = 0; k < net[i].Neuron[j].Weights.Length; k++)//для каждого веса

{

if (i == 0)

{

net[i].Neuron[j].Weights[k] -= temp

* data[curr].Input[k] + Regularization * net[i].Neuron[j].Weights[k] / data.Count;

}

else

{

net[i].Neuron[j].Weights[k] -= temp

* net[i - 1].Neuron[k].LastState + Regularization * net[i].Neuron[j].Weights[k] / data.Count;

}

}

}

}

}

/// <summary>

/// Расчет ошибки скрытых слоев

/// </summary>

/// <param name="net">сеть</param>

private static void HiddenlayerError(MultiLayer net)

{

for (int k = net.Length - 2; k >= 0; k--)

{

//для каждого нейрона

for (int j = 0; j < net[k].Neuron.Length; j++)

{

net.Layer[k].Neuron[j].CurrentError = 0;

//сумма по всем нейронам следующего слоя

for (int i = 0; i < net[k + 1].Neuron.Length; i++)

{

//errorj = sum_for_i(errori * F'(Sj) * wij)

net.Layer[k].Neuron[j].CurrentError += net.Layer[k + 1].Neuron[i].CurrentError

* net.Layer[k + 1].Neuron[i].Weights[j] *

net.Layer[k + 1].Neuron[i].ActivationFunction.Derivative(net.Layer[k + 1].Neuron[i].LastPulse);

}

}

}

}

/// <summary>

/// Расчет ошибки выходного слоя

/// </summary>

/// <param name="data">обучающая выборка</param>

/// <param name="net">сеть</param>

/// <param name="curr">номер текущего образа</param>

///

private void OutputLayerError(IList<NeuroImage<double>> data, MultiLayer net, int curr)

{

int last = net.Layer.Length - 1;

//ошибка выходного слоя по каждому нейрону

for (int j = 0; j < net.Layer[last].Neuron.Length; j++)

{

net.Layer[last].Neuron[j].CurrentError = 0;

net.Layer[last].Neuron[j].CurrentError = ErrorFunction.Derivative(net.Layer[last].Neuron[j].LastState, data[curr].Output[j]);

}

}

private void RandomShuffle(ref IList<NeuroImage<double>> data)

{

Random gen = new Random();

int ind1, ind2;

for (int i = 0; i < data.Count; i++)

{

ind1 = gen.Next(0, data.Count);

ind2 = gen.Next(0, data.Count);

Swap(data, ind1, ind2);

}

}

private void Swap(IList<NeuroImage<double>> data, int ind1, int ind2)

{

NeuroImage<double> temp = data[ind1];

data[ind1] = data[ind2];

data[ind2] = temp;

}

private bool Compare(double[] p1, double[] p2)

{

double error = 0.0;

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

{

error += (p1[i] - p2[i]) * (p1[i] - p2[i]);

}

error /= 2;

if (error < OneImageMinError)

return true;

else

return false;

}

}

Приложение Б:

private void cloudButton1_Click(object sender, EventArgs e)

{

UpdateParametrizedSymbol();

toolStripProgressBar1.ProgressBar.Show();

Application.DoEvents();

source.Colors.Clear();

source.Text = null;

Task.Run(

() =>

{

Bitmap image = new Bitmap(pictureBox1.Image);

if (System.IO.File.Exists("SymbolRecognise.json") == false)

{

MessageBox.Show("Ошибка! Не найден файл SymbolRecognise.json");

return;

}

if (System.IO.File.Exists("ColorRecognise.json") == false)

{

MessageBox.Show("Ошибка! Не найден файл ColorRecognise.json");

return;

}

var SymbolNetwork = Neuro.MLP.MultiLayerNeuralNetwork.Load("SymbolRecognise.json");

var ColorNetwork = Neuro.MLP.MultiLayerNeuralNetwork.Load("ColorRecognise.json");

System.Drawing.Size blockSize = new Size(10, 10);

System.Drawing.Size metrics = Work.BitmapWorker.Round(image.Size, blockSize);

if (source.DefaultZoom == true)

{

metrics = Work.BitmapWorker.RecalculateSize(image.Size, source.WidthValue);

image = Work.BitmapWorker.Zoom(image, metrics);

}

if (image.Height % blockSize.Height != 0)

{

metrics = Work.BitmapWorker.Round(image.Size, blockSize);

image = Work.BitmapWorker.Resize(image, metrics);

}

int blockPerLine = Convert.ToInt32((float)image.Width / (float)blockSize.Width);

int BlockAmount = Convert.ToInt32((float)image.Width / (float)blockSize.Width * (float)image.Height / (float)blockSize.Height);

double[] input = new double[100];

Bitmap ImageToEncoding;

if (source.UsePreworking == true)

ImageToEncoding = Work.BitmapWorker.ColorToGray(image);

else

ImageToEncoding = image;

Bitmap temp, encodeTemp;

Rectangle rect;

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

{

rect = GetBlockRect(image, i);

temp = Work.BitmapWorker.Cut(image, rect);

encodeTemp = Work.BitmapWorker.Cut(ImageToEncoding, rect);

if (source.SixteenColorMode == true)

{

source.Colors.Add(ColorNetwork.Compute(temp));

}

else

{

source.Colors.Add(temp.GetPixel(5, 5));

}

input = CreateInput(encodeTemp);

var res = SymbolNetwork.Compute(input);

double max = res.Max();

int index = res.ToList().IndexOf(max);

source.Text += source.Images[index];

if (source.Doublicate)

{

source.Text += source.Images[index];

source.Colors.Add(source.Colors.Last());

}

if (i % blockPerLine == blockPerLine - 1)

source.Text += '\n';

}

Form2 dlg = new Form2(source.Text, source.Colors);

toolStripProgressBar1.ProgressBar.InvokeIfRequired(() =>

{

// Do anything you want with the control here

toolStripProgressBar1.ProgressBar.Hide();

});

dlg.ShowDialog();

}

);

}