Разработка редактора растровых изображений

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

Рубрика Программирование, компьютеры и кибернетика
Вид практическая работа
Язык русский
Дата добавления 28.09.2019
Размер файла 1006,7 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://allbest.ru

Задание на практику

1. Подготовить исходные данные (изображения) для проведения эксперимента;

2. Написать программное обеспечение, выполняющее:

- открытие исходных изображений;

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

- подсчет числа получившихся объектов и их геометрических характеристик.

3. Провести эксперимент и зафиксировать его результаты.

4. Проанализировать результаты эксперимента.

Выполнение заданий

1. Исходные данные представляют собой файлы специального формата (NIFTI) - серии изображений компьютерной томографии органов человека. Для проведения эксперимента взяли по одному изображению из пяти категорий: Легкое, Печень, Селезенка, Сердце, Толстая кишка. Для преобразования файлов из формата nii в png было использовано специальное программное обеспечение Aliza.

2. Программное обеспечение было написано с использованием библиотек машинного зрения АForge.Net. Оно позволяет загружать из файлов и просматривать исходные изображения, а также изображения с шумом, изображения, полученные в результате бинаризации и в результате выделения контуров. Для текущего просматриваемого изображения можно в таблицу вывести все его контура и их геометрические характеристики. Для анализа результатов эксперимента программа выводит графики зависимости таких характеристик как количество контуров, суммы площадей контуров и суммы периметров контуров от номера изображения для исходных изображений и изображений с шумом (рисунки 4 - 8).

Ниже приведен текст программы

программный шум четкость изображение

XAML - разметка основного окна программы

<Window x:Class="НИР.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:НИР"

mc:Ignorable="d"

Title="НИР" Height="600" Width="1000"

Icon="Resources/Icon.png"

WindowStartupLocation="CenterScreen" MinWidth="600" MinHeight="400">

<Grid Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}">

<DockPanel>

<StackPanel DockPanel.Dock="Top">

<Menu>

<MenuItem Header="Выбор папки с изображениями" Click="Load_Click"/>

<MenuItem x:Name="Parameters" Header="Параметры контуров" IsEnabled="False" Click="Parameters_Click"/>

<MenuItem x:Name="Analiz" Header="Анализ результатов" IsEnabled="False" Click="Analiz_Click"/>

</Menu>

</StackPanel>

<Grid DockPanel.Dock="Bottom">

<Grid.ColumnDefinitions>

<ColumnDefinition/>

<ColumnDefinition Width="100"/>

</Grid.ColumnDefinitions>

<Slider Grid.Column="0" Margin="10"

Value="{Binding Val, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Maximum="{Binding Max, Mode=OneWay}"

Minimum="1" ValueChanged="Slider_ValueChanged"/>

<TextBox Grid.Column="1"

IsReadOnly="True"

Margin="10"

Text="{Binding Val}"/>

</Grid>

<TabControl DockPanel.Dock="Left">

<TabControl.Resources>

<ControlTemplate x:Key="ImageItem" TargetType="{x:Type Label}">

<Canvas x:Name="MyCanvas" ToolTip="{Binding FileName, Mode=OneWay}" MouseUp="MyCanvas_MouseUp">

<Canvas.Background>

<ImageBrush ImageSource="{Binding BlobImage, UpdateSourceTrigger=PropertyChanged}" Stretch="Uniform"/>

</Canvas.Background>

</Canvas>

<ControlTemplate.Triggers>

<MultiDataTrigger>

<MultiDataTrigger.Conditions>

<Condition Binding ="{Binding ElementName = dataGrid, Path = SelectedIndex}" Value ="-1"/>

<Condition Binding ="{Binding ElementName = NoiceDataGrid, Path = SelectedIndex}" Value ="-1"/>

</MultiDataTrigger.Conditions>

<Setter TargetName="MyCanvas" Property="Background">

<Setter.Value>

<ImageBrush ImageSource="{Binding FileName, UpdateSourceTrigger=PropertyChanged}" Stretch="Uniform"/>

</Setter.Value>

</Setter>

</MultiDataTrigger>

</ControlTemplate.Triggers>

</ControlTemplate>

<Style TargetType="DataGrid">

<Setter Property="IsReadOnly" Value="True"/>

<Setter Property="AutoGenerateColumns" Value="False"/>

<Setter Property="RowHeaderWidth" Value="0"/>

</Style>

</TabControl.Resources>

<TabItem x:Name="Image" Header="Исходное избражение" GotFocus="Image_GotFocus">

<Grid>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TabControl Grid.Column="0">

<TabItem x:Name="OriginalImage" Header="Оригинал" GotFocus="Original_GotFocus">

<Label Template="{DynamicResource ImageItem}"/>

</TabItem>

<TabItem x:Name="BinaryImage" Header="Бинарное" GotFocus="Binary_GotFocus">

<Label Template="{DynamicResource ImageItem}"/>

</TabItem>

<TabItem x:Name="ContourImage" Header="Контуры" GotFocus="Contour_GotFocus">

<Label x:Name="ContourLabel" Template="{DynamicResource ImageItem}" SizeChanged="ContourLabel_SizeChanged"/>

</TabItem>

</TabControl>

<DataGrid Grid.Column="1" ItemsSource="{Binding BlobList}" x:Name="dataGrid" SelectionChanged="DataGrid_SelectionChanged">

<DataGrid.Columns>

<DataGridTemplateColumn CanUserSort="False" Header="Контур">

<DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<Image Source="{Binding Bmp}" Width="100" Height="100"/>

</DataTemplate>

</DataGridTemplateColumn.CellTemplate>

</DataGridTemplateColumn>

<DataGridTextColumn Binding="{Binding Area}" CanUserSort="False" Header="Площадь"/>

<DataGridTextColumn Binding="{Binding Perimeter}" CanUserSort="False" Header="Периметр"/>

<DataGridTextColumn Binding="{Binding CenterOfGravity}" CanUserSort="False" Header="Центр тяжести"/>

<DataGridTextColumn Binding="{Binding Fullness}" CanUserSort="False" Header="Наполненность"/>

<DataGridTextColumn Binding="{Binding Width}" CanUserSort="False" Header="Ширина"/>

<DataGridTextColumn Binding="{Binding Height}" CanUserSort="False" Header="Высота"/>

</DataGrid.Columns>

</DataGrid>

</Grid>

</TabItem>

<TabItem x:Name="NoiceImage" Header="Изображение с шумом" GotFocus="Noice_GotFocus">

<Grid>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TabControl>

<TabItem x:Name="NoiceOriginalImage" Header="Оригинал" GotFocus="Noice_Original_GotFocus">

<Label Template="{DynamicResource ImageItem}"/>

</TabItem>

<TabItem x:Name="NoiceBinaryImage" Header="Бинарное" GotFocus="Noice_Binary_GotFocus">

<Label Template="{DynamicResource ImageItem}"/>

</TabItem>

<TabItem x:Name="NoiceContourImage" Header="Контуры" GotFocus="Noice_Contour_GotFocus">

<Label x:Name="NoiceContourLabel" Template="{DynamicResource ImageItem}" SizeChanged="NoiceContourLabel_SizeChanged"/>

</TabItem>

</TabControl>

<DataGrid Grid.Column="1" ItemsSource="{Binding BlobList}" x:Name="NoiceDataGrid" SelectionChanged="NoiceDataGrid_SelectionChanged">

<DataGrid.Columns>

<DataGridTemplateColumn CanUserSort="False" Header="Контур">

<DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<Image Source="{Binding Bmp}" Width="100" Height="100"/>

</DataTemplate>

</DataGridTemplateColumn.CellTemplate>

</DataGridTemplateColumn>

<DataGridTextColumn Binding="{Binding Area}" CanUserSort="False" Header="Площадь"/>

<DataGridTextColumn Binding="{Binding Perimeter}" CanUserSort="False" Header="Периметр"/>

<DataGridTextColumn Binding="{Binding CenterOfGravity}" CanUserSort="False" Header="Центр тяжести"/>

<DataGridTextColumn Binding="{Binding Fullness}" CanUserSort="False" Header="Наполненность"/>

<DataGridTextColumn Binding="{Binding Width}" CanUserSort="False" Header="Ширина"/>

<DataGridTextColumn Binding="{Binding Height}" CanUserSort="False" Header="Высота"/>

</DataGrid.Columns>

</DataGrid>

</Grid>

</TabItem>

</TabControl>

</DockPanel>

</Grid>

</Window>

Обработчик событий основного окна программы

namespace НИР

{

/// <summary>

/// Логика взаимодействия для MainWindow.xaml

/// </summary>

public partial class MainWindow : Window

{

Processing Proc, NoiseProc; // Объекты обработки изображений

public MainWindow()

{

InitializeComponent();

DataContext = new ViewClass();

}

// Обработка выбора папки с файлами

private void Load_Click(object sender, RoutedEventArgs e)

{

// Выбор каталога с изображениями в диалоговом окне

FolderBrowserDialog FBD = new FolderBrowserDialog();

if (FBD.ShowDialog() == System.Windows.Forms.DialogResult.OK)

{

((ViewClass)DataContext).FileName = null;

// Создание объекта обработки исходных изображений

Proc = new Processing(FBD.SelectedPath);

// Подписка на событие ошибки

Proc.OnException += Proc_OnException;

// Запуск обработки исходных изображений

Proc.Run();

// Создание каталога изображений с шумом

string path = Proc.GetNoiseImages();

// Создание объекта обработки изображений с шумом

NoiseProc = new Processing(path);

// Подписка на событие ошибки

NoiseProc.OnException += Proc_OnException;

// Запуск обработки изображений с шумом

NoiseProc.Run();

// Отображение первого изображения в каталоге

((ViewClass)DataContext).Val = 1;

((ViewClass)DataContext).Max = Proc.Filenames.Count;

((ViewClass)DataContext).FileName = Proc.Filenames[0];

Image.IsSelected = true;

OriginalImage.IsSelected = true;

System.Windows.MessageBox.Show("Для просмотра всех изображений выберите " +

"соответствующую вкладку и перемещайте ползунок");

Parameters.IsEnabled = true;

Analiz.IsEnabled = true;

}

}

// Обработка вычисления параметров контуров

private void Parameters_Click(object sender, RoutedEventArgs e)

{

if (Image.IsSelected)

{

ContourImage.Focus();

Proc.GetBlobs(((ViewClass)DataContext).FileName);

((ViewClass)DataContext).BlobList = Proc.BlobsParameters;

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

DrawRectangles(can, Proc);

}

else

{

NoiceContourImage.Focus();

NoiseProc.GetBlobs(((ViewClass)DataContext).FileName);

((ViewClass)DataContext).BlobList = NoiseProc.BlobsParameters;

Canvas can = (Canvas) NoiceContourLabel.Template.FindName("MyCanvas", NoiceContourLabel);

DrawRectangles(can, NoiseProc);

}

}

// Метод рисования прямоугольника ограничивающего контур на холсте

private void DrawRectangle(Canvas can, BlobParameters bp)

{

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

double ww = can.ActualWidth,

hw = can.ActualHeight,

wi = bp.Bmp.Width,

hi = bp.Bmp.Height,

x = 0,

y = 0,

w = 0,

h = 0;

if (hw / ww >= hi / wi)

{

x = ww * bp.X / wi;

y = (hw - ww * hi / wi) / 2 + ww * bp.Y / wi;

w = ww * bp.Width / wi;

h = ww * bp.Height / wi;

}

else

{

y = hw * bp.Y / hi;

x = (ww - hw * wi / hi) / 2 + hw * bp.X / hi;

w = hw * bp.Width / hi;

h = hw * bp.Height / hi;

}

// Создание объекта прямоугольника

Rectangle rect = new Rectangle()

{

Width = w,

Height = h,

Stroke = Brushes.Blue

};

// Помещение прямоугольника на холст

can.Children.Add(rect);

Canvas.SetLeft(rect, x);

Canvas.SetTop(rect, y);

}

// Метод рисования прямоугольников ограничивающих все контуры

private void DrawRectangles(Canvas can, Processing proc)

{

if (can != null)

{

can.Children.Clear();

if (proc.BlobsParameters != null)

foreach (BlobParameters bp in proc.BlobsParameters)

{

DrawRectangle(can, bp);

}

}

}

#region Обработчики выбора вкладок

private void Original_GotFocus(object sender, RoutedEventArgs e)

{

if(Proc!=null)

((ViewClass)DataContext).FileName =

Proc.Filenames[((ViewClass)DataContext).Val-1];

dataGrid.SelectedIndex = -1;

NoiceDataGrid.SelectedIndex = -1;

}

private void Binary_GotFocus(object sender, RoutedEventArgs e)

{

if (Proc != null)

{

string path = Proc.SelectedPath + "\\Binary";

((ViewClass)DataContext).FileName =

Proc.Filenames[((ViewClass)DataContext).Val - 1]

.Replace(Proc.SelectedPath, path);

}

dataGrid.SelectedIndex = -1;

NoiceDataGrid.SelectedIndex = -1;

}

private void Contour_GotFocus(object sender, RoutedEventArgs e)

{

if (Proc != null)

{

string path = Proc.SelectedPath + "\\Contour";

((ViewClass)DataContext).FileName =

Proc.Filenames[((ViewClass)DataContext).Val - 1]

.Replace(Proc.SelectedPath, path);

// Рисование прямоугольников

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

DrawRectangles(can, Proc);

}

}

private void Noice_Original_GotFocus(object sender, RoutedEventArgs e)

{

if (NoiseProc != null)

((ViewClass)DataContext).FileName =

NoiseProc.Filenames[((ViewClass)DataContext).Val-1];

dataGrid.SelectedIndex = -1;

NoiceDataGrid.SelectedIndex = -1;

}

private void Noice_Binary_GotFocus(object sender, RoutedEventArgs e)

{

if (NoiseProc != null)

{

string path = NoiseProc.SelectedPath + "\\Binary";

((ViewClass)DataContext).FileName =

NoiseProc.Filenames[((ViewClass)DataContext).Val - 1]

.Replace(NoiseProc.SelectedPath, path);

}

dataGrid.SelectedIndex = -1;

NoiceDataGrid.SelectedIndex = -1;

}

private void Noice_Contour_GotFocus(object sender, RoutedEventArgs e)

{

if (NoiseProc != null)

{

string path = NoiseProc.SelectedPath + "\\Contour";

((ViewClass)DataContext).FileName =

NoiseProc.Filenames[((ViewClass)DataContext).Val - 1]

.Replace(NoiseProc.SelectedPath, path);

// Рисование прямоугольников

Canvas can = (Canvas) NoiceContourLabel.Template.FindName ("MyCanvas", NoiceContourLabel);

DrawRectangles(can, NoiseProc);

}

}

private void Image_GotFocus(object sender, RoutedEventArgs e)

{

if (Proc != null)

{

((ViewClass)DataContext).BlobList = Proc.BlobsParameters;

// Рисование прямоугольников

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

DrawRectangles(can, Proc);

}

}

private void Noice_GotFocus(object sender, RoutedEventArgs e)

{

if (NoiseProc != null)

{

((ViewClass)DataContext).BlobList = NoiseProc.BlobsParameters;

// Рисование прямоугольников

Canvas can = (Canvas) NoiceContourLabel.Template.FindName ("MyCanvas", NoiceContourLabel);

DrawRectangles(can, NoiseProc);

}

}

#endregion

// Обработка выбора контура без шума в таблице

private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

if (((ViewClass)DataContext).BlobList != null && dataGrid.SelectedIndex >= 0)

{

((ViewClass)DataContext).BlobImage = ((ViewClass)DataContext).BlobList[dataGrid.SelectedIndex].Bmp;

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

if (can != null)

{

can.Children.Clear();

if (Proc.BlobsParameters != null)

DrawRectangle(can, ((ViewClass)DataContext).BlobList[dataGrid.SelectedIndex]);

}

}

}

// Обработка выбора контура с шумом в таблице

private void NoiceDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

if (((ViewClass)DataContext).BlobList != null && NoiceDataGrid.SelectedIndex >= 0)

{

((ViewClass)DataContext).BlobImage = ((ViewClass)DataContext).BlobList[NoiceDataGrid.SelectedIndex].Bmp;

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", NoiceContourLabel);

if (can != null)

{

can.Children.Clear();

if (NoiseProc.BlobsParameters != null)

DrawRectangle(can, ((ViewClass)DataContext).BlobList[NoiceDataGrid.SelectedIndex]);

}

}

}

// Обработка клика по окну с изображением

// Сброс выбранного в таблице контура

private void MyCanvas_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)

{

dataGrid.SelectedIndex = -1;

NoiceDataGrid.SelectedIndex = -1;

// Рисование прямоугольников

if (Image.IsSelected)

{

if (Proc != null)

{

Canvas can = (Canvas) ContourLabel.Template.FindName("MyCanvas", ContourLabel);

DrawRectangles(can, Proc);

}

}

else

{

if (NoiseProc != null)

{

Canvas can = (Canvas) NoiceContourLabel.Template.FindName("MyCanvas", NoiceContourLabel);

DrawRectangles(can, NoiseProc);

}

}

}

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

private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

{

if (Image != null && ((ViewClass)DataContext).FileName != null)

{

string oldFile = ((ViewClass)DataContext).FileName;

string newFile = Proc.Filenames[((ViewClass)DataContext).Val - 1];

newFile = oldFile.Substring(0, oldFile.LastIndexOf('\\'))

+ newFile.Substring(newFile.LastIndexOf('\\'));

((ViewClass)DataContext).FileName = newFile;

Proc.ClearStat();

NoiseProc.ClearStat();

((ViewClass)DataContext).BlobList = null;

// Очистка холстов от прямоугольников

Canvas can;

can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

if (can != null)

can.Children.Clear();

can = (Canvas)NoiceContourLabel.Template.FindName("MyCanvas", NoiceContourLabel);

if (can != null)

can.Children.Clear();

}

}

// Вызов окна графического анализа параметров контуров

private void Analiz_Click(object sender, RoutedEventArgs e)

{

Proc.GetAnalize();

NoiseProc.GetAnalize();

Analize NewWindow = new Analize(Proc.AnalizeResults, NoiseProc.AnalizeResults)

{

Owner = this

};

NewWindow.ShowDialog();

}

// Обработка изменения размеров области для рисования контура без шума

private void ContourLabel_SizeChanged(object sender, SizeChangedEventArgs e)

{

if(Image.IsSelected && ContourImage.IsSelected)

{

if (Proc != null)

{

Canvas can = (Canvas)ContourLabel.Template.FindName("MyCanvas", ContourLabel);

if (dataGrid.SelectedIndex < 0)

DrawRectangles(can, Proc);

else

{

if (can != null)

{

can.Children.Clear();

if (Proc.BlobsParameters != null)

DrawRectangle(can, ((ViewClass)DataContext).BlobList[dataGrid.SelectedIndex]);

}

}

}

}

}

// Обработка изменения размеров области для рисования контура с шумом

private void NoiceContourLabel_SizeChanged(object sender, SizeChangedEventArgs e)

{

if (NoiceImage.IsSelected && NoiceContourImage.IsSelected)

{

if (NoiseProc != null)

{

Canvas can = (Canvas)NoiceContourLabel.Template.FindName("MyCanvas", NoiceContourLabel);

if (NoiceDataGrid.SelectedIndex < 0)

DrawRectangles(can, NoiseProc);

else

{

if (can != null)

{

can.Children.Clear();

if (NoiseProc.BlobsParameters != null)

DrawRectangle(can, ((ViewClass)DataContext).BlobList[NoiceDataGrid.SelectedIndex]);

}

}

}

}

}

// Обработка ошибок в объектах обработки изображений

private void Proc_OnException(string Message)

{

System.Windows.MessageBox.Show(Message);

}

}

}

Класс обработки изображений

namespace НИР

{

public delegate void OnExceptionHandler(string text);

// Класс обработки изображений

public class Processing

{

private List<string> filenames; // Список обрабатываемых файлов

private string selectedPath; // Текущий каталог с файлами

private List<BlobParameters> blobsParameters;

private List<AnalizeResult> analizeResults;

#region Public properties

public List<string> Filenames

{ get { return filenames; } }

public string SelectedPath

{ get { return selectedPath; } }

public List<BlobParameters> BlobsParameters

{ get { return blobsParameters; } }

public List<AnalizeResult> AnalizeResults

{ get { return analizeResults; } }

#endregion

// Конструктор

public Processing(string path)

{

filenames = Directory.GetFiles(path, "*.png").ToList();

selectedPath = path;

}

// Метод запуска обработки изображений

public void Run()

{

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

string path = selectedPath + "\\Binary";

Directory.CreateDirectory(path);

path = selectedPath + "\\Contour";

Directory.CreateDirectory(path);

path = selectedPath;

try

{

foreach (string filename in filenames)

{

if (!File.Exists(filename.Replace(selectedPath, selectedPath + "\\Binary")) &&

!File.Exists(filename.Replace(selectedPath, selectedPath + "\\Contour")))

{

// Создание объектов фильтров

Grayscale filterGray = new Grayscale(0.2125, 0.7154, 0.0721);

GrayscaleToRGB filterRGB = new GrayscaleToRGB();

IterativeThreshold binary = new IterativeThreshold();

HomogenityEdgeDetector contour = new HomogenityEdgeDetector();

// Считывание изображения из файла

Bitmap image = new Bitmap(filename);

// Получение бинарного изображений

Bitmap binaryImage = binary.Apply(filterGray.Apply(image));

// Получение контура

Bitmap contourImage = contour.Apply(binaryImage);

// Сохранение бинарного изображения

path = selectedPath + "\\Binary";

image = filterRGB.Apply(binaryImage);

binaryImage.Dispose();

image.Save(filename.Replace(selectedPath, path),

System.Drawing.Imaging.ImageFormat.Png);

// Сохранение контура

path = selectedPath + "\\Contour";

image = filterRGB.Apply(contourImage);

contourImage.Dispose();

image.Save(filename.Replace(selectedPath, path),

System.Drawing.Imaging.ImageFormat.Png);

image.Dispose();

// Периодическая очистка памяти

if (GC.GetTotalMemory(true) > 1000000000) GC.Collect();

}

}

}

catch (Exception ex)

{

OnException(ex.Message);

}

}

// Метод создания каталога изображений с шумом

public string GetNoiseImages()

{

// Создание каталога для изображений с шумом

string path = selectedPath + "\\Noise";

Directory.CreateDirectory(path);

try

{

foreach (string filename in filenames)

{

string newfilename = filename.Replace(selectedPath, path);

if (!File.Exists(newfilename))

{

// Создание объектов фильтров

Grayscale filterGray = new Grayscale(0.2125, 0.7154, 0.0721);

GrayscaleToRGB filterRGB = new GrayscaleToRGB();

AdaptiveSmoothing noise = new AdaptiveSmoothing();

// Считывание изображения из файла

Bitmap image = new Bitmap(filename);

// Получение изображения с шумом

image = filterRGB.Apply(noise.Apply(filterGray.Apply(image)));

// Сохранение изображения с шумом

image.Save(newfilename, System.Drawing.Imaging.ImageFormat.Png);

image.Dispose();

// Периодическая очистка памяти

if (GC.GetTotalMemory(true) > 1000000000) GC.Collect();

}

}

}

catch (Exception ex)

{

OnException(ex.Message);

}

return path;

}

// Получение параметров контуров

public void GetBlobs(string filename)

{

// Создание объектов фильтров

BlobCounter bc = new BlobCounter();

Grayscale filterGray = new Grayscale(0.2125, 0.7154, 0.0721);

try

{

// Считывание изображения из файла

Bitmap image = new Bitmap(filename);

image = filterGray.Apply(image);

bc.ProcessImage(image);

Blob[] blobs = bc.GetObjects(image, true);

blobsParameters = new List<BlobParameters>();

foreach (Blob blob in blobs)

{

blobsParameters.Add(new BlobParameters(blob, bc));

}

image.Dispose();

}

catch (Exception ex)

{

OnException(ex.Message);

}

}

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

public void GetAnalize ()

{

string path = selectedPath + "\\Contour";

analizeResults = new List<AnalizeResult>();

try

{

foreach (string _filename in filenames)

{

// Создание объектов фильтров

BlobCounter bc = new BlobCounter();

Grayscale filterGray = new Grayscale(0.2125, 0.7154, 0.0721);

string filename = _filename.Replace(selectedPath, path);

// Считывание изображения из файла

Bitmap image = new Bitmap(filename);

image = filterGray.Apply(image);

bc.ProcessImage(image);

Blob[] blobs = bc.GetObjects(image, true);

image.Dispose();

int sumAreas = 0;

int sumPerimeters = 0;

foreach (Blob blob in blobs)

{

sumAreas += blob.Area;

sumPerimeters += bc.GetBlobsEdgePoints(blob).Count;

}

analizeResults.Add(new AnalizeResult(blobs.Length, sumAreas, sumPerimeters));

// Периодическая очистка памяти

if (GC.GetTotalMemory(true)>1000000000) GC.Collect();

}

}

catch (Exception ex)

{

OnException(ex.Message);

}

}

// Очистка данных о параметрах контуров

public void ClearStat()

{

blobsParameters = null;

}

// Событие ошибки в программе

public event OnExceptionHandler OnException;

}

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

public class BlobParameters

{

private BitmapImage bmp;

private int area;

private int perimeter;

private AForge.Point centerOfGravity;

private double fullness;

private int width;

private int height;

private int x;

private int y;

#region Открытые свойства

public BitmapImage Bmp

{ get { return bmp; } }

public int Area

{ get { return area; } }

public int Perimeter

{ get { return perimeter; } }

public AForge.Point CenterOfGravity

{ get { return centerOfGravity; } }

public double Fullness

{ get { return fullness; } }

public int Width

{ get { return width; } }

public int Height

{ get { return height; } }

public int X

{ get { return x; } }

public int Y

{ get { return y; } }

#endregion

// Конструктор

public BlobParameters(Blob blob, BlobCounter bc)

{

Bitmap bmpBlob = blob.Image.ToManagedImage();

bmp = BitmapToImageSource(bmpBlob);

area = blob.Area;

perimeter = bc.GetBlobsEdgePoints(blob).Count;

centerOfGravity = blob.CenterOfGravity;

fullness = blob.Fullness;

width = blob.Rectangle.Width;

height = blob.Rectangle.Height;

x = blob.Rectangle.X;

y = blob.Rectangle.Y;

}

// Преобразование изображения из Bitmap в BitmapImage

private BitmapImage BitmapToImageSource(Bitmap bitmap)

{

using (MemoryStream memory = new MemoryStream())

{

bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png);

memory.Position = 0;

BitmapImage bitmapimage = new BitmapImage();

bitmapimage.BeginInit();

bitmapimage.StreamSource = memory;

bitmapimage.CacheOption = BitmapCacheOption.OnLoad;

bitmapimage.EndInit();

return bitmapimage;

}

}

}

// Класс параметров изображения для графического анализа

public class AnalizeResult

{

private int contoursCount; // Количество контуров

private int sumAreas; // Сумма площадей контуров

private int sumPerimeters; // Сумма периметров контуров

#region Открытые свойства

public int ContoursCount

{ get { return contoursCount; } }

public int SumAreas

{ get { return sumAreas; } }

public int SumPerimeters

{ get { return sumPerimeters;} }

#endregion

// Конструктор

public AnalizeResult(int _count, int _sumAreas, int _sumPerim)

{

contoursCount = _count;

sumAreas = _sumAreas;

sumPerimeters = _sumPerim;

}

}

}

XAML-разметка окна вывода графиков

<Window x:Class="НИР.Analize"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:НИР"

mc:Ignorable="d"

Title="Анализ результатов" Height="600" Width="800"

Icon="Resources/Icon2.png"

WindowStartupLocation="CenterScreen" MinWidth="400" MinHeight="300">

<Grid SizeChanged="Grid_SizeChanged">

<TabControl x:Name="TabCa" SelectionChanged="TabCa_SelectionChanged">

<TabControl.Resources>

<Style TargetType="Line">

<Setter Property="StrokeThickness" Value="2"/>

</Style>

<Style TargetType="Polyline">

<Setter Property="StrokeThickness" Value="2"/>

</Style>

</TabControl.Resources>

<TabItem Header="Количество контуров" x:Name="NumCont">

<Border Margin="10" BorderThickness="2"

BorderBrush="DarkGray">

<Grid>

<Canvas x:Name="CanvasNum" Margin="10" Loaded="CanvasNum_Loaded">

<Polyline Name="NumLine" Stroke="DarkBlue"

Points="{Binding Graphici}"/>

<Polyline Name="NoiceNumLine" Stroke="Orange"

Points="{Binding GraphiciNoice}"/>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TextBlock Grid.Row ="0" Grid.Column="0">

Без шума

</TextBlock>

<TextBlock Grid.Row ="1" Grid.Column="0">

С шумом

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="0"

HorizontalAlignment="Right">

Max =

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="1"

Text="{Binding MaxValue}"

HorizontalAlignment="Left"/>

<Line Grid.Row ="0" Grid.Column="1" Stroke="DarkBlue"

X1="5" X2="40" Y1="10" Y2="10" />

<Line Grid.Row ="1" Grid.Column="1" Stroke="Orange"

X1="5" X2="40" Y1="10" Y2="10"/>

</Grid>

<Line X1="0" X2="0" Y1="40" Stroke="Black" StrokeThickness="1"

Y2="{Binding ElementName=CanvasNum, Path=ActualHeight}"/>

<Line X1="0" Stroke="Black" StrokeThickness="1"

X2="{Binding ElementName=CanvasNum, Path=ActualWidth}"

Y2="{Binding ElementName=CanvasNum, Path=ActualHeight}"

Y1="{Binding ElementName=CanvasNum, Path=ActualHeight}"/>

</Canvas>

</Grid>

</Border>

</TabItem>

<TabItem Header="Сумма площадей контуров" x:Name="AreaCont">

<Border Margin="10" BorderThickness="2"

BorderBrush="DarkGray">

<Grid>

<Canvas x:Name="CanvasArea" Margin="10" Loaded="CanvasArea_Loaded">

<Polyline Name="AreaLine" Stroke="DarkBlue"

Points="{Binding Graphici}"/>

<Polyline Name="NoiceAreaLine" Stroke="Orange"

Points="{Binding GraphiciNoice}"/>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TextBlock Grid.Row ="0" Grid.Column="0">

Без шума

</TextBlock>

<TextBlock Grid.Row ="1" Grid.Column="0">

С шумом

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="0"

HorizontalAlignment="Right">

Max =

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="1"

Text="{Binding MaxValue}"

HorizontalAlignment="Left"/>

<Line Grid.Row ="0" Grid.Column="1" Stroke="DarkBlue"

X1="5" X2="40" Y1="10" Y2="10" />

<Line Grid.Row ="1" Grid.Column="1" Stroke="Orange"

X1="5" X2="40" Y1="10" Y2="10"/>

</Grid>

<Line X1="0" X2="0" Y1="40" Stroke="Black" StrokeThickness="1"

Y2="{Binding ElementName=CanvasArea, Path=ActualHeight}"/>

<Line X1="0" Stroke="Black" StrokeThickness="1"

X2="{Binding ElementName=CanvasArea, Path=ActualWidth}"

Y2="{Binding ElementName=CanvasArea, Path=ActualHeight}"

Y1="{Binding ElementName=CanvasArea, Path=ActualHeight}"/>

</Canvas>

</Grid>

</Border>

</TabItem>

<TabItem Header="Сумма периметров контуров" x:Name="PerimCont">

<Border Margin="10" BorderThickness="2"

BorderBrush="DarkGray">

<Grid>

<Canvas x:Name="CanvasPerimeter" Margin="10" Loaded="CanvasPerimeter_Loaded">

<Polyline Name="PerimLine" Stroke="DarkBlue"

Points="{Binding Graphici}"/>

<Polyline Name="NoicePerimLine" Stroke="Orange"

Points="{Binding GraphiciNoice}"/>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TextBlock Grid.Row ="0" Grid.Column="0">

Без шума

</TextBlock>

<TextBlock Grid.Row ="1" Grid.Column="0">

С шумом

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="0"

HorizontalAlignment="Right">

Max =

</TextBlock>

<TextBlock Grid.Row ="2" Grid.Column="1"

Text="{Binding MaxValue}"

HorizontalAlignment="Left"/>

<Line Grid.Row ="0" Grid.Column="1" Stroke="DarkBlue"

X1="5" X2="40" Y1="10" Y2="10" />

<Line Grid.Row ="1" Grid.Column="1" Stroke="Orange"

X1="5" X2="40" Y1="10" Y2="10"/>

</Grid>

<Line X1="0" X2="0" Y1="40" Stroke="Black" StrokeThickness="1"

Y2="{Binding ElementName=CanvasPerimeter, Path=ActualHeight}"/>

<Line X1="0" Stroke="Black" StrokeThickness="1"

X2="{Binding ElementName=CanvasPerimeter, Path=ActualWidth}"

Y2="{Binding ElementName=CanvasPerimeter, Path=ActualHeight}"

Y1="{Binding ElementName=CanvasPerimeter, Path=ActualHeight}"/>

</Canvas>

</Grid>

</Border>

</TabItem>

</TabControl>

</Grid>

</Window>

Обработчик событий окна вывода графиков

namespace НИР

{

/// <summary>

/// Логика взаимодействия для Analize.xaml

/// </summary>

public partial class Analize : Window

{

List<AnalizeResult> Graph;

List<AnalizeResult> NoiceGraph;

public Analize(List<AnalizeResult> graph, List<AnalizeResult> noiceGraph)

{

InitializeComponent();

Graph = new List<AnalizeResult>(graph);

NoiceGraph = new List<AnalizeResult>(noiceGraph);

DataContext = new ViewClass();

NumCont.Focus();

}

private void CanvasNum_Loaded(object sender, RoutedEventArgs e)

{

if (NumCont.IsSelected)

{

List<int> _value = new List<int>(Graph.Select(x => x.ContoursCount));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.ContoursCount));

GetPoints(_value, _noiceValue, CanvasNum.ActualHeight, CanvasNum.ActualWidth);

}

}

private void CanvasArea_Loaded(object sender, RoutedEventArgs e)

{

if (AreaCont.IsSelected)

{

List<int> _value = new List<int>(Graph.Select(x => x.SumAreas));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.SumAreas));

GetPoints(_value, _noiceValue, CanvasArea.ActualHeight, CanvasArea.ActualWidth);

}

}

private void CanvasPerimeter_Loaded(object sender, RoutedEventArgs e)

{

if (PerimCont.IsSelected)

{

List<int> _value = new List<int>(Graph.Select(x => x.SumPerimeters));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.SumPerimeters));

GetPoints(_value, _noiceValue, CanvasPerimeter.ActualHeight, CanvasPerimeter.ActualWidth);

}

}

private void GetPoints(List<int> _value, List<int> _noiceValue, double canH, double canW)

{

int maxG = _value.Max();

int maxNG = _noiceValue.Max();

double maxValue = maxG > maxNG ? maxG : maxNG;

((ViewClass)DataContext).MaxValue = (int)maxValue;

double maxHeight = canH - 40.0;

double maxWidth = canW;

var _Graphici = new PointCollection();

var _GraphiciNoice = new PointCollection();

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

{

_Graphici.Add(new Point(maxWidth * i / _value.Count, maxHeight + 40 - maxHeight * _value[i] / maxValue));

_GraphiciNoice.Add(new Point(maxWidth * i / _noiceValue.Count, maxHeight + 40 - maxHeight * _noiceValue[i] / maxValue));

}

((ViewClass)DataContext).Graphici = _Graphici;

((ViewClass)DataContext).GraphiciNoice = _GraphiciNoice;

}

private void TabCa_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

if (NumCont.IsSelected)

{

if (CanvasNum.Parent != null)

{

var parent = (Panel)CanvasNum.Parent;

parent.Children.Remove(CanvasNum);

parent.Children.Add(CanvasNum);

}

}

else if (AreaCont.IsSelected)

{

if (CanvasArea.Parent != null)

{

var parent = (Panel)CanvasArea.Parent;

parent.Children.Remove(CanvasArea);

parent.Children.Add(CanvasArea);

}

}

else

{

if (CanvasPerimeter.Parent != null)

{

var parent = (Panel)CanvasPerimeter.Parent;

parent.Children.Remove(CanvasPerimeter);

parent.Children.Add(CanvasPerimeter);

}

}

}

private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)

{

if (NumCont.IsSelected)

{

List<int> _value = new List<int>(Graph.Select(x => x.ContoursCount));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.ContoursCount));

GetPoints(_value, _noiceValue, CanvasNum.ActualHeight, CanvasNum.ActualWidth);

}

else if (AreaCont.IsSelected)

{

List<int> _value = new List<int>(Graph.Select(x => x.SumAreas));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.SumAreas));

GetPoints(_value, _noiceValue, CanvasArea.ActualHeight, CanvasArea.ActualWidth);

}

else

{

List<int> _value = new List<int>(Graph.Select(x => x.SumPerimeters));

List<int> _noiceValue = new List<int>(NoiceGraph.Select(x => x.SumPerimeters));

GetPoints(_value, _noiceValue, CanvasPerimeter.ActualHeight, CanvasPerimeter.ActualWidth);

}

}

}

}

Класс свойств зависимостей

namespace НИР

{

public class ViewClass : DependencyObject

{

public string FileName

{

get { return (string)GetValue(FileNameProperty); }

set { SetValue(FileNameProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства FileName

public static readonly DependencyProperty FileNameProperty =

DependencyProperty.Register("FileName", typeof(string), typeof(ViewClass), new PropertyMetadata(null));

public int Max

{

get { return (int)GetValue(MaxProperty); }

set { SetValue(MaxProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства Max

public static readonly DependencyProperty MaxProperty =

DependencyProperty.Register("Max", typeof(int), typeof(ViewClass), new PropertyMetadata(1));

public int Val

{

get { return (int)GetValue(ValProperty); }

set { SetValue(ValProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства Val

public static readonly DependencyProperty ValProperty =

DependencyProperty.Register("Val", typeof(int), typeof(ViewClass), new PropertyMetadata(1));

public List<BlobParameters> BlobList

{

get { return (List<BlobParameters>)GetValue(BlobListProperty); }

set { SetValue(BlobListProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства BlobList

public static readonly DependencyProperty BlobListProperty =

DependencyProperty.Register("BlobList", typeof(List<BlobParameters>), typeof(ViewClass), new PropertyMetadata(null));

public BitmapImage BlobImage

{

get { return (BitmapImage)GetValue(BlobImageProperty); }

set { SetValue(BlobImageProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства BlobImage

public static readonly DependencyProperty BlobImageProperty =

DependencyProperty.Register("BlobImage", typeof(BitmapImage), typeof(ViewClass), new PropertyMetadata(null));

public PointCollection Graphici

{

get { return (PointCollection)GetValue(GraphiciProperty); }

set { SetValue(GraphiciProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства Graphici

public static readonly DependencyProperty GraphiciProperty =

DependencyProperty.Register("Graphici", typeof(PointCollection), typeof(ViewClass), new PropertyMetadata(null));

public PointCollection GraphiciNoice

{

get { return (PointCollection)GetValue(GraphiciNoiceProperty); }

set { SetValue(GraphiciNoiceProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства GraphiciNoice

public static readonly DependencyProperty GraphiciNoiceProperty =

DependencyProperty.Register("GraphiciNoice", typeof(PointCollection), typeof(ViewClass), new PropertyMetadata(null));

public int MaxValue

{

get { return (int)GetValue(MaxValueProperty); }

set { SetValue(MaxValueProperty, value); }

}

// Использование DependencyProperty в качестве резервного хранилища для свойства MaxValue

public static readonly DependencyProperty MaxValueProperty =

DependencyProperty.Register("MaxValue", typeof(int), typeof(ViewClass), new PropertyMetadata(0));

}

}

Результаты эксперимента

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

Рисунок 1 - Исходное изображение кишки без шума и с шумом

Рисунок 2 - Бинарное изображение кишки без шума и с шумом

Рисунок 3 - Контуры изображения кишки без шума и с шумом

Таблица 1 - Геометрические характеристики изображения без шума

Таблица 2 - Геометрические характеристики изображения с шумом

Рисунок 4 - Анализ контуров изображений кишки с шумом и без шума

Для других категорий изображений выведем результаты эксперимента только в виде графиков

Рисунок 5 - Анализ контуров изображений легких с шумом и без шума

Рисунок 6 - Анализ контуров изображений печени с шумом и без шума

Рисунок 7 - Анализ контуров изображений селезенки с шумом и без шума

Рисунок 8 - Анализ контуров изображений сердца с шумом и без шума

Анализ результатов эксперимента

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

- изображения получают более четкие очертания крупных объектов изображений, мелкие же детали изображений пропадают;

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

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

Размещено на Allbest.ru


Подобные документы

  • Общая информация о графическом формате. Описание формата Microsoft Windows Bitmap. Структура файла DDВ исходного формата ВМР. Преобразования графических файлов. Просмотр и редактирование растровых изображений. Создание многодокументного приложения.

    дипломная работа [1,5 M], добавлен 06.06.2010

  • Методы обработки растровых изображений (кластеризация, пороговая и интерактивная сегментация). Разработка программного модуля для системы мониторинга биосферы и дистанционного зондирования. Создание пользовательского интерфейса программного модуля.

    курсовая работа [2,2 M], добавлен 29.04.2015

  • Обзор существующего программного обеспечения для автоматизации выделения границ на изображении. Разработка математической модели обработки изображений и выделения контуров в оттенках серого и программного обеспечения для алгоритмов обработки изображений.

    дипломная работа [1,7 M], добавлен 27.03.2013

  • Описание математических методов представления и обработки графических изображений. Описание разработанного программного дополнения. Описание функций и их атрибутов. Представление и обработка графических изображений. Результаты тестирования программы.

    курсовая работа [1,7 M], добавлен 27.01.2015

  • Анализ проблем, возникающих при совмещении изображений в корреляционно-экстремальных навигационных системах. Использование двумерного дискретного преобразования Фурье. Нахождение корреляционной функции радиолокационного и моделируемого изображений.

    дипломная работа [3,6 M], добавлен 07.07.2012

  • Разработка программы для создания, просмотра и сохранения изображений. Реализация функции рисования различных фигур с заливкой и без заливки, функции очистки рабочего пространства и отмены последних действий. Обоснование выбранных методов и алгоритмов.

    курсовая работа [3,2 M], добавлен 25.07.2013

  • Задача пространственно-временной обработки изображений при наличии шумов и помех. Методы оптимизации при пространственно-временной обработке изображений. Структура специализированной программы, описание ее пользовательского интерфейса. Смета затрат.

    дипломная работа [957,2 K], добавлен 10.06.2013

  • Технологические возможности компьютерных программ для работы с графикой. Режимы и источники изображений, основной и фоновый цвет, контуры, фильтры и меню графического редактора Adobe Photoshop. Создание и трансформация объектов в программе Corel Draw.

    курсовая работа [39,5 K], добавлен 08.11.2013

  • Обработка изображений на современных вычислительных устройствах. Устройство и представление различных форматов изображений. Исследование алгоритмов обработки изображений на базе различных архитектур. Сжатие изображений на основе сверточных нейросетей.

    дипломная работа [6,1 M], добавлен 03.06.2022

  • Загрузка интерфейса изображением формата хранения растровых изображений BMP. Программа осуществления отражения изображения по вертикали и горизонтали. Применение к изображению черно-белого, сглаживающего, подчеркивания границ и медианного фильтров.

    лабораторная работа [713,6 K], добавлен 26.04.2015

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.