Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






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

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

 

Требования к работе:

В системе следует предусмотреть следующие возможности:

1) изменение пользователем значения порога;

2) вычисление площади сегментированных объектов;

3) выделение на изображении сегментированного объекта.

 

Содержание отчета:

– титульный лист;

– задание к работе;

– теоретическая информация по реализованным методам сегментации с описанием их достоинств и недостатков;

– результаты работы в виде экранных форм;

– листинг программы.

 



1 ОПИСАНИЕ РЕАЛИЗОВАННЫХ МЕТОДОВ СЕГМЕНТАЦИИ

 

1.1 Пороговая сегментация

 

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

где f0 - некоторое " пороговое" значение яркости.

При выполнении пороговой обработки основной вопрос состоит в выборе порога f0. Пусть полутоновое изображение содержит интересующие нас объекты одной яркости на фоне другой яркости (типичные примеры: машинописный текст, чертежи, медицинские пробы под микроскопом и т. д.). Тогда в идеале плотность распределения яркостей должна выглядеть как две дельта-функции. В данном случае задача установления порога тривиальна: в качестве f0 можно взять любое значение между " пиками". В результате пики функции плотности распределения " расплываются", хотя обычно ее бимодальность сохраняется. В такой ситуации можно выбрать порог f0, соответствующий положению минимума между модами, то есть использовать функцию поэлементного преобразования.

 

1.2 Алгоритм водораздела

 

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

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

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

– введение минимальной глубины бассейна;

– предварительная фильтрация для удаления мелких неоднородностей;

– уменьшение числа уровней квантования;

– уменьшение степени детализации изображения.

Достоинства: отслеживание не только границ, но и частичное восстановление отсутствующих деталей на изображении.

 

ЭКРАННЫЕ ФОРМЫ

 

Рисунок 2.1 – Выполнение пороговой сегментации с порогом 128

 

Рисунок 2.2 – Выполнение сегментации методом водораздела с порогом 128

 

 

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

 

3.1 Методы статического класса Utils

 

using System;

using System.Drawing;

using System.Drawing.Imaging;

using System.Windows.Forms;

using System.Runtime.InteropServices;

 

namespace IR_Lab

{

public static class Utils

{

// создание дочернего окна (с возвратом указателя на это окно)

public static Form_ChildPicture CreateNewChild(string FormText, string FileName, Form mdiParent, Bitmap FormImage, bool isGray, bool isModified)

{

Form_ChildPicture child_frm = new Form_ChildPicture();

child_frm.Text = FormText;

child_frm.fileName = FileName;

child_frm.MdiParent = mdiParent;

child_frm.pictureBox.Image = FormImage;

child_frm.bmpImage = FormImage;

child_frm.isGray = isGray;

child_frm.isModified = isModified;

child_frm.Show();

return child_frm;

}

 

// обработка Bitmap - выделение сегментов

public static int[] ProcessSegment(Bitmap _in, int type, int param, ref int[, ] pxSegmentMap, ref int[] pixels_arr)

{

// выходной массив площадей сегментов

int[] pxSegmentSquare = null;

// задаём формат пикселя

PixelFormat pxf = PixelFormat.Format24bppRgb;

// получаем данные картинки

Rectangle rect = new Rectangle(0, 0, _in.Width, _in.Height);

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

BitmapData bmpData = _in.LockBits(rect, ImageLockMode.ReadWrite, pxf);

// получаем адрес первой линии

IntPtr ptr = bmpData.Scan0;

// задаём массив из Byte и помещаем в него набор данных.

int numBytes = _in.Width * _in.Height * 3;

byte[] rgbValues = new byte[numBytes];

// копируем значения в массив.

Marshal.Copy(ptr, rgbValues, 0, numBytes);

// выбираем задание

switch (type)

{

case 0:

{ pxSegmentSquare = segByBorder(ref rgbValues, ref pxSegmentMap, _in.Width, _in.Height, param); } break;

case 1:

{ pxSegmentSquare = segWatershed(ref rgbValues, ref pxSegmentMap, _in.Width, _in.Height, param, ref pixels_arr); } break;

}

// Копируем набор данных обратно в изображение

Marshal.Copy(rgbValues, 0, ptr, numBytes);

// Разблокируем набор данных изображения в памяти.

_in.UnlockBits(bmpData);

 

return pxSegmentSquare;

}

 

 

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

public static int[] segByBorder(ref byte[] rgbValues, ref int[, ] pxSegmentMap, int width, int height, int param)

{

int point;

int[] pxSegmentSquare = new int[2] { 0, 0 };

for (int j = 0; j < height; j++)

{

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

{

point = (j * width + i) * 3;

if ((int)rgbValues[point] < = param)

{

pxSegmentSquare[0]++;

pxSegmentMap[i, j] = 0;

}

else

{

pxSegmentSquare[1]++;

pxSegmentMap[i, j] = 1;

}

}

}

return pxSegmentSquare;

}

 

 

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

public static int[] segWatershed(ref byte[] rgbValues, ref int[, ] pxSegmentMap, int width, int height, int param, ref int[] pixels_arr)

{

int point;

 

// " очистка" сегментной карты

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

{

for (int j = 0; j < height; j++)

{

pxSegmentMap[i, j] = -1;

}

}

 

// сначала найдем максимум и минимум по яркостям (для оптимизации)

int max = 255, min = 0;

bool min_found = false, max_found = false;

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

{

if (! min_found & & pixels_arr[i] > 0)

{

min = i;

min_found = true;

}

if (! max_found & & pixels_arr[255 - i] > 0)

{

max = 255 - i;

max_found = true;

}

if (min_found & & max_found)

break;

}

 

// глубины бассейнов и их количество

int[] poolsDepth = new int[width * height];

int poolsCount = 0;

for (int i = 0; i < width * height; i++)

poolsDepth[i] = 0;

 

// другие важные переменные:)

int curr_light = min;

int curr_pool = -1;

bool neighbourExist = false;

int check_code = 0;

int change_seg_from = 0, change_seg_to = 0;

 

// " гуляем" по картинке (итерации по каждому из цветов)

while (curr_light < = max)

{

// если вообще есть пиксели с данной яркостью на рисунке

if (pixels_arr[curr_light] > 0)

{

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

for (int j = 0; j < height; j++)

{

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

{

point = (j * width + i) * 3;

// находим пиксель

if ((int)rgbValues[point] == curr_light)

{

// смотрим соседей пикселя и подбираем ему бассейн (анализ " крестиком")

curr_pool = -1;

neighbourExist = false;

// левый сосед

if ((i - 1) > = 0 & & (pxSegmentMap[i - 1, j]! = -1))

{

if (curr_pool == -1)

curr_pool = pxSegmentMap[i - 1, j];

else if (poolsDepth[pxSegmentMap[i - 1, j]] > = poolsDepth[curr_pool])

curr_pool = pxSegmentMap[i - 1, j];

neighbourExist = true;

}

// верхний сосед

if ((j - 1) > = 0 & & (pxSegmentMap[i, j - 1]! = -1))

{

if (curr_pool == -1)

curr_pool = pxSegmentMap[i, j - 1];

else if (poolsDepth[pxSegmentMap[i, j - 1]] > = poolsDepth[curr_pool])

curr_pool = pxSegmentMap[i, j - 1];

neighbourExist = true;

}

// правый сосед

if ((i + 1) < width & & (pxSegmentMap[i + 1, j]! = -1))

{

if (curr_pool == -1)

curr_pool = pxSegmentMap[i + 1, j];

else if (poolsDepth[pxSegmentMap[i + 1, j]] > = poolsDepth[curr_pool])

curr_pool = pxSegmentMap[i + 1, j];

neighbourExist = true;

}

// нижний сосед

if ((j + 1) < height & & (pxSegmentMap[i, j + 1]! = -1))

{

if (curr_pool == -1)

curr_pool = pxSegmentMap[i, j + 1];

else if (poolsDepth[pxSegmentMap[i, j + 1]] > = poolsDepth[curr_pool])

curr_pool = pxSegmentMap[i, j + 1];

neighbourExist = true;

}

 

// если соседа не нашли -> он становится новым бассейном

if (! neighbourExist)

{

pxSegmentMap[i, j] = poolsCount;

poolsDepth[poolsCount] = (int)rgbValues[point];

poolsCount++;

}

else // иначе - присваиваем найденный ближайший бассейн и анализируем соседей

{

pxSegmentMap[i, j] = curr_pool;

// анализируем опять же " крестиком"

check_code = 0;

if (j > 0 & & j < height - 1 & & i > 0 & & i < width - 1)

check_code = 3;

if ((j == 0 || j == height - 1) & & i > 0 & & i < width - 1)

check_code = 2;

if (j > 0 & & j < height - 1 & & (i == 0 || i == width - 1))

check_code = 1;

// проверка вертикали

if (check_code == 1 || check_code == 3)

{

if (pxSegmentMap[i, j - 1] > = 0 & & pxSegmentMap[i, j + 1] > = 0 & & pxSegmentMap[i, j - 1]! = pxSegmentMap[i, j + 1])

{

// проверка глубин

if (Math.Abs(poolsDepth[pxSegmentMap[i, j - 1]] - poolsDepth[pxSegmentMap[i, j + 1]]) < = param)

{

if (poolsDepth[pxSegmentMap[i, j - 1]] < poolsDepth[pxSegmentMap[i, j + 1]])

{

change_seg_from = pxSegmentMap[i, j + 1];

change_seg_to = pxSegmentMap[i, j - 1];

}

else

{

change_seg_from = pxSegmentMap[i, j - 1];

change_seg_to = pxSegmentMap[i, j + 1];

}

ChangeSegmentCode(ref pxSegmentMap, width, height, change_seg_from, change_seg_to);

}

}

}

// проверка горизонтали

if (check_code == 2 || check_code == 3)

{

if (pxSegmentMap[i - 1, j] > = 0 & & pxSegmentMap[i + 1, j] > = 0 & & pxSegmentMap[i - 1, j]! = pxSegmentMap[i + 1, j])

{

// проверка глубин

if (Math.Abs(poolsDepth[pxSegmentMap[i - 1, j]] - poolsDepth[pxSegmentMap[i + 1, j]]) < = param)

{

if (poolsDepth[pxSegmentMap[i - 1, j]] < poolsDepth[pxSegmentMap[i + 1, j]])

{

change_seg_from = pxSegmentMap[i + 1, j];

change_seg_to = pxSegmentMap[i - 1, j];

}

else

{

change_seg_from = pxSegmentMap[i - 1, j];

change_seg_to = pxSegmentMap[i + 1, j];

}

ChangeSegmentCode(ref pxSegmentMap, width, height, change_seg_from, change_seg_to);

}

}

}

}

}

}

}

}

curr_light++;

}

 

// обнуляем массив глубин бассейнов - будем его использовать для подсчета пикселей каждого бассейна

for (int i = 0; i < width * height; i++)

poolsDepth[i] = 0;

 

// повторный просмотр карты сегментов - выделение границ и подсчет пикселей

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

{

for (int j = 0; j < height; j++)

{

// считаем пиксели определенного сегмента

poolsDepth[pxSegmentMap[i, j]]++;

// просматриваем его соседей (здесь уже все варианты)

check_code = 0;

if (j > 0 & & j < height - 1 & & i > 0 & & i < width - 1)

check_code = 3;

if ((j == 0 || j == height - 1) & & i > 0 & & i < width - 1)

check_code = 2;

if (j > 0 & & j < height - 1 & & (i == 0 || i == width - 1))

check_code = 1;

// проверка вертикали

if (check_code == 1 || check_code == 3)

{

if (pxSegmentMap[i, j - 1] > = 0 & & pxSegmentMap[i, j + 1] > = 0 & & pxSegmentMap[i, j - 1]! = pxSegmentMap[i, j + 1])

{

pxSegmentMap[i, j] = -1;

}

}

// проверка горизонтали

if (check_code == 2 || check_code == 3)

{

if (pxSegmentMap[i - 1, j] > = 0 & & pxSegmentMap[i + 1, j] > = 0 & & pxSegmentMap[i - 1, j]! = pxSegmentMap[i + 1, j])

{

pxSegmentMap[i, j] = -1;

}

}

// проверка диагоналей

if (check_code == 3)

{

if (pxSegmentMap[i - 1, j - 1] > = 0 & & pxSegmentMap[i + 1, j + 1] > = 0 & & pxSegmentMap[i - 1, j - 1]! = pxSegmentMap[i + 1, j + 1])

{

pxSegmentMap[i, j] = -1;

}

else if (pxSegmentMap[i - 1, j + 1] > = 0 & & pxSegmentMap[i + 1, j - 1] > = 0 & & pxSegmentMap[i - 1, j + 1]! = pxSegmentMap[i + 1, j - 1])

{

pxSegmentMap[i, j] = -1;

}

}

}

}

// массив " площадей" сегментов

int[] pxSegmentSquare = null;

poolsCount = 0;

for (int i = 0; i < width * height; i++)

{

if (poolsDepth[i] > 0)

{

poolsCount++;

if (pxSegmentSquare == null)

{

pxSegmentSquare = new int[1] { poolsDepth[i] };

}

else

{

Array.Resize(ref pxSegmentSquare, poolsCount);

pxSegmentSquare[poolsCount - 1] = poolsDepth[i];

}

}

}

return pxSegmentSquare;

}

 

 

// изменение кодировки сегментов (при соединении двух бассейнов)

private static void ChangeSegmentCode(ref int[, ] pxSegmentMap, int width, int height, int from, int to)

{

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

{

for (int j = 0; j < height; j++)

{

if (pxSegmentMap[i, j] == from)

pxSegmentMap[i, j] = to;

}

}

}

 

}

}

 

 

3.2 Форма параметров сегментации Form_SegmentParams

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

namespace IR_Lab

{

public partial class Form_SegmentParams: Form

{

public Form_ChildPicture process_frm;

 

public Form_SegmentParams()

{

InitializeComponent();

}

 

private void Form_SegmentParams_FormClosing(object sender, FormClosingEventArgs e)

{

e.Cancel = true;

Height = 156;

Hide();

}

 

private void radioButton1_CheckedChanged(object sender, EventArgs e)

{

label1.Text = " Значение порога сегментации: ";

}

 

private void radioButton2_CheckedChanged(object sender, EventArgs e)

{

label1.Text = " Значение минимальной глубины бассейна: ";

}

 

private void btnApply_Click(object sender, EventArgs e)

{

int type = 0;

if (radioButton2.Checked)

type = 1;

 

if ((int)nudParam.Value > 0)

{

process_frm.pxSegmentMap = new int[process_frm.pictureBox.Image.Width, process_frm.pictureBox.Image.Height];

for (int i = 0; i < process_frm.pictureBox.Image.Width; i++)

{

for (int j = 0; j < process_frm.pictureBox.Image.Height; j++)

{

process_frm.pxSegmentMap[i, j] = -1;

}

}

int[] pxSegmentSquare = Utils.ProcessSegment(new Bitmap(process_frm.pictureBox.Image), type, Convert.ToInt16(nudParam.Value), ref process_frm.pxSegmentMap, ref process_frm.pixels_arr);

process_frm.segmentMapExist = true;

process_frm.pictureBox.Refresh();

 

rtbSquares.Clear();

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

{

rtbSquares.AppendText(" Сегмент " + (i+1).ToString() + ". Кол-во пикселей: " + pxSegmentSquare[i].ToString() + " \n");

}

Height = 363;

}

else

{

MessageBox.Show(" Вы ввели неверные параметры сегментации! ", " Ошибка выполнения действия", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

 

 

private void Form_SegmentParams_VisibleChanged(object sender, EventArgs e)

{

if (process_frm.segmentMapExist)

{

Height = 363;

}

else

{

Height = 156;

}

}

}

}

 

 

ВЫВОДЫ

 

В результате выполнения работы были изучены алгоритмы сегментации изображения и выполнена реализация алгоритмов пороговой сегментации и водораздела на языке высокого уровня C# (.NET 2.0).

<== предыдущая лекция | следующая лекция ==>
Приложение с несколькими формами | Создание проекта. Основные окна среды.
Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.055 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал