Детекција контура објеката на слици

/******************************************************************************************************

* Назив програма : Детекција контура објеката на слици *

* Верзија програма : 1.0 *

* Креиран у пакету : IDE SharpDevelop ver. 4.4 *

* Аутор програма : Перић Жељко *

* Програмски језик : C#, вер. 4.0 *

* Датум : 17.04.2015 - 03.05.2015 *

* *

* *

* Опис програма : Програм проналази контуре (ивице) објеката на слици *

* упамћеној у JPЕG или BMP стандардном 24 битном RGB формату. *

* *

* Детекција ивичних тачака се одрађује алгоритмом који представља варијацију *

* "Difference Edge Detection" алгоритма приказаног на сајту Code PROJECT од стране *

* аутора Кристиан Грауса ( Christian Graus , software developer, Australia ). *

* *

* *

* Овај алгоритам се заснива на претпоставци да се контура (ивица) објекта на слици *

* уочава голим оком уколико постоји значајна разлика боје између парова наспрамних *

* тачака на слици које су позициониране непосредно око посматране тачке која припада *

* ивици посматраног објекта. Мисли се на парове тачака који могу да формирају праву *

* линију која пролази кроз посматрану тачку. *

* *

* 0 0 0 0 1 0 1 0 0 0 0 1 2 - посматрана тачка *

* 1 2 1 0 2 0 0 2 0 0 2 0 1 - пар тачака *

* 0 0 0 0 1 0 0 0 1 1 0 0 *

* *

* У суштини, врши се проналажење максималне вредности разлике боје за четири пара *

* тачака и уколико је та вредност различита од унапред дефинисане,посматраној тачки *

* се додељује другачија вредност за боју. На резултујућој слици та тачка постаје *

* ивична тачка. На горе наведеном линку можете погледати имплементацију алгоритма *

* у програмском језику C#, која показује одличан резултат при детекцији ивица *

* објеката на слици, као и још неколико алгоритама исте намене. *

* *

* Варијација предходног алгоритма се састоји у томе што се уместо вредности боје, *

* посматра вредност осветљености парова тачака на слици. Уколико је максимална *

* разлика осветљености парова тачака мања од вредности доње границе осветљености *

* тачка мења своју основну боју у црну, а уколико је већа од вредности горње границе *

* осветљености тачка мења своју основну боју у белу. *

* *

* Контроле у програму : *

* *

* - Кликом на оригиналну слику отвара се дијалог за учитавање нове слике *

* - Кликом на резултујућу слику отвара се дијалог за снимање нове слике *

* - Променом граничних вредности осветљености тачке, аутоматски се покреће *

* нова обрада оригиналне слике и добија се нови резултат *

* - Променом избора врсте приказа обрађене слике, аутоматски се покреће *

* нова обрада оригиналне слике и добија се нови резултат *

* *

* *

* *

* Све најбоље, *

* Аутор *

* *

******************************************************************************************************/


using System;

using System.Drawing;

using System.Drawing.Imaging;

using System.Runtime.InteropServices;

using System.Windows.Forms;


namespace Bitmapa_u_2d_matricu

{

/// <summary>

/// Description of MainForm.

/// </summary>

public partial class MainForm : Form

{

#region- Декларација глобалних променљивих -

//

// Променљива за памћење оригиналне слике

Bitmap Оригинална_слика;

// Програм обрађује искључиво слике стандардног формата JPEG или BMP,

// код којих је свакој тачки за дефинисање боје додељено двадесетчетири бита,

// осам бита ( један бајт ) за сваку компоненту RGB боје пиксела.

const PixelFormat Дозвољен_RGB_Формат_Тачке = PixelFormat.Format24bppRgb;

//

#endregion

public MainForm()

{

//

// The InitializeComponent() call is required for Windows Forms designer support.

//

InitializeComponent();

}

#region- Улазно излазни део програма -

void Слика_1_Клик(object sender, EventArgs e)

{

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

// одабир слике за обраду, детекцију контура

DialogResult Избор = Учитај_Слику_Дијалог.ShowDialog();

Refresh();

if(Избор == DialogResult.OK)

{

string Формат_Документа = Учитај_Слику_Дијалог.FileName.Substring(Учитај_Слику_Дијалог.FileName.Length-3,3).ToUpper();

if(Формат_Документа == "BMP" || Формат_Документа == "JPG")

Оригинална_слика = new Bitmap(Учитај_Слику_Дијалог.FileName);

else

MessageBox.Show(" Одабрали сте документ чији формат не одговара подржаним форматима ( JPEG или BMP ). ",

" Грешка! ",

MessageBoxButtons.OK,

MessageBoxIcon.Exclamation);


if(Оригинална_слика!=null && Оригинална_слика.PixelFormat == Дозвољен_RGB_Формат_Тачке)

{

Слика_1.Image = Оригинална_слика;

Пронађи_Контуре();

}

else

Оригинална_слика = null;

}

Refresh();

}

void Слика_2_Клик(object sender, EventArgs e)

{

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

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

bool Избор_ОК = false;

if(Слика_2.Image!=null)

{

while(!Избор_ОК)

{

DialogResult Избор = Сними_Слику_Дијалог.ShowDialog();

Refresh();

if(Избор == DialogResult.OK && Сними_Слику_Дијалог.FileName!=Учитај_Слику_Дијалог.FileName)

{

string Формат_Документа = Учитај_Слику_Дијалог.FileName.Substring(Учитај_Слику_Дијалог.FileName.Length-3,3).ToUpper();

if(Формат_Документа=="BMP")

{

Слика_2.Image.Save(Сними_Слику_Дијалог.FileName,ImageFormat.Bmp);

Избор_ОК = true;

}

else if(Формат_Документа=="JPG")

{

Слика_2.Image.Save(Сними_Слику_Дијалог.FileName,ImageFormat.Jpeg);

Избор_ОК = true;

}

else

MessageBox.Show(" Одабрали сте документ чији формат не одговара подржаним форматима ( JPEG или BMP ). ",

" Грешка! ",

MessageBoxButtons.OK,

MessageBoxIcon.Exclamation);

}

else if(Избор == DialogResult.OK && Сними_Слику_Дијалог.FileName==Учитај_Слику_Дијалог.FileName)

MessageBox.Show(" Одабрали сте документ који је тренутно у употреби \n" +

" и није могуће снимање података у њега. ",

" Грешка! ",

MessageBoxButtons.OK,

MessageBoxIcon.Exclamation);

else

Избор_ОК = true;

}

}

}

#endregion

#region- Главни део програма -

//

void Детекција_Контура()

{

// Копирај оригиналну слику у нову битмапу

Bitmap bmp = (Bitmap)Оригинална_слика.Clone();


int Ширина_слике = bmp.Width;

int Висина_слике = bmp.Height;

// Величина оквира

Rectangle Оквир = new Rectangle(0, 0, Ширина_слике, Висина_слике);

// Закључај битмапу у меморији због брже обраде података, и преузми податке.

BitmapData bmp_Подаци = bmp.LockBits(Оквир, ImageLockMode.ReadWrite, bmp.PixelFormat);

// Учитај адресу првог бајта битмапе

// Поинтер на адресу првог бајта битмапе у меморији

IntPtr bmp_Адреса_првог_бајта = bmp_Подаци.Scan0;


//

// Декларација матрице која треба да садржи све бајтове битмапе.

//

// Битмапа мора да садржи 24 бита (три бајта) по пикселу,

// по један бајт за сваку компоненту RGB боје пиксела.

int Број_тачака = Ширина_слике * Висина_слике;

int Број_бајтова = Број_тачака * 3;

//

// Због начина памћења података битмапе,поравнања бајтова у реду,

// број бајтова у реду се заокружује на први већи број дељив са четири,

// тако да један ред битмапе увек садржи исти или већи број бајтова

// од оног који је неопходан за стваран број тачака у реду.

int Број_бајтова_у_реду = bmp_Подаци.Stride;

int Број_тачака_у_реду = Ширина_слике*3;

//

int Број_бајтова_поравнања_у_реду = Број_бајтова_у_реду - Број_тачака_у_реду;

//

// Укупан број бајтова неопходан за памћење података битмапе

Број_бајтова += Висина_слике * Број_бајтова_поравнања_у_реду;

//

// Једнодимензионална матрица за памћење

// вредности RGB компоненти боје тачака битмапе

byte[] bmp_RGB_vrednosti = new byte[Број_бајтова];

// Копирај вредности RGB компоненти боје тачака из битмапе у матрицу.

Marshal.Copy(bmp_Адреса_првог_бајта, bmp_RGB_vrednosti, 0, Број_бајтова);

// Дводимензионална матрица за памћење

// вредности RGB компоненти боје тачака битмапе

byte [,,] RGB = new byte[Ширина_слике,Висина_слике,3];

// Матрица за памћење вредности осветљења тачака слике

float [,] Осветљење = new float[Ширина_слике,Висина_слике];

// Бројач бајтова у једнодимензионалној матрици

int bmp_k = 0;

// Копирај вредности RGB компоненти боје тачака из

// једнодимензионалне у дводимензионалну матрицу

// и попуни матрицу Осветљење вредностима јачине осветљења тачака.

//

// Напомена :

// Приликом преузимања података битмапе

// BitmapData Подаци_битмапе = bmp.LockBits(...,...,...) ,

// вредности RGB компоненти боје тачке су преузете у обрнутом редоследу.

// RGB -> BGR

//

for (int i=0;i<Висина_слике;i++)

{

for(int j=0;j<Ширина_слике;j++)

{

// Вредност R компоненте боје тачке

RGB[j,i,0] = bmp_RGB_vrednosti[bmp_k+2];

// Вредност G компонентњ боје тачке

RGB[j,i,1] = bmp_RGB_vrednosti[bmp_k+1];

// Вредност B компонентњ боје тачке

RGB[j,i,2] = bmp_RGB_vrednosti[bmp_k+0];

// Вредност осветљеости тачке

Осветљење[j,i] = Color.FromArgb

(

bmp_RGB_vrednosti[bmp_k+2],

bmp_RGB_vrednosti[bmp_k+1],

bmp_RGB_vrednosti[bmp_k+0]

).GetBrightness();

bmp_k+=3;

}

bmp_k+= Број_бајтова_поравнања_у_реду;

}



// Учитај доњу и горњу гранцу вредности осветљења тачке

float доња_граница = (float) Доња_Граница_Осветљења.Value;

float горња_граница = (float) Горња_Граница_Осветљења.Value;

// Максимална разлика осветљености парова тачака

float мро = 0;

for(int i=1;i<Висина_слике-1;i++)

{

for(int j=1;j<Ширина_слике-1;j++)

{

//

мро = Math.Abs(Осветљење[j-1,i-1]-Осветљење[j+1,i+1]);

//

if(мро<Math.Abs(Осветљење[j-1,i+1]-Осветљење[j+1,i-1]))

мро=Math.Abs(Осветљење[j-1,i+1]-Осветљење[j+1,i-1]);

//

if(мро<Math.Abs(Осветљење[j,i+1]-Осветљење[j,i-1]))

мро=Math.Abs(Осветљење[j,i+1]-Осветљење[j,i-1]);

//

if(мро<Math.Abs(Осветљење[j-1,i]-Осветљење[j+1,i]))

мро=Math.Abs(Осветљење[j-1,i]-Осветљење[j+1,i]);

//

if(Инвертуј_Боје_Ивице.Checked)

{

if(мро<доња_граница)

{

RGB[j,i,0] = (byte) 255;

RGB[j,i,1] = (byte) 255;

RGB[j,i,2] = (byte) 255;

}

else if(мро>горња_граница)

{

RGB[j,i,0] = (byte) 0;

RGB[j,i,1] = (byte) 0;

RGB[j,i,2] = (byte) 0;

}

}

else

{

if(мро<доња_граница)

{

RGB[j,i,0] = (byte) 0;

RGB[j,i,1] = (byte) 0;

RGB[j,i,2] = (byte) 0;

}

else if(мро>горња_граница)

{

RGB[j,i,0] = (byte) 255;

RGB[j,i,1] = (byte) 255;

RGB[j,i,2] = (byte) 255;

}

}

}

}

if(Црно_Бело.Checked)

{

for(int i=1;i<Висина_слике-1;i++)

{

for(int j=1;j<Ширина_слике-1;j++)

{

if(Инвертуј_Боје_Ивице.Checked)

{

if(RGB[j,i,0] < 255 || RGB[j,i,1] < 255 || RGB[j,i,2] < 255)

RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] = (byte) 0;

}

else

{

if(RGB[j,i,0] > 0 || RGB[j,i,1] > 0 || RGB[j,i,2] > 0)

RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] = (byte) 255;

}

}

}

}

if(Нијансе_Сиве.Checked)

{

for(int i=1;i<Висина_слике-1;i++)

{

for(int j=1;j<Ширина_слике-1;j++)

{

RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] =

(byte)

(

(0.299*RGB[j,i,0]) +

(0.587*RGB[j,i,1]) +

(0.114*RGB[j,i,2])

);

}

}

}

// Бројач бајтова у једнодимензионалној матрици

bmp_k = 0;

// Копирај нове вредности RGB компоненти боје тачака из

// дводимензионалне у једнодимензионалну матрицу

for (int i=0;i<Висина_слике;i++)

{

for(int j=0;j<Ширина_слике;j++)

{

// Вредност R компоненте боје пиксела

bmp_RGB_vrednosti[bmp_k+2] = RGB[j,i,0];

// Вредност G компоненте боје пиксела

bmp_RGB_vrednosti[bmp_k+1] = RGB[j,i,1];

// Вредност B компоненте боје пиксела

bmp_RGB_vrednosti[bmp_k+0] = RGB[j,i,2];

bmp_k+=3;

}

bmp_k+=Број_бајтова_поравнања_у_реду;

}

// Копирај нове вредности RGB компоненти боје тачака из матрице назад у битмапу

Marshal.Copy(bmp_RGB_vrednosti, 0, bmp_Адреса_првог_бајта, Број_бајтова);

// Откључај битмапу

bmp.UnlockBits(bmp_Подаци);

// Прикажи обрађену битмапу као резултујућу слику

Слика_2.Image = bmp;

}

//

#endregion


#region- Контролни део програма -

void Пронађи_Контуре()

{

// Уколико је учитана оригинална слика

// покрени детекцију контура и прикажи резултујућу слику

if(Оригинална_слика!=null)

Детекција_Контура();

}

void Инвертуј_Боје_CheckedChanged(object sender, EventArgs e)

{

Пронађи_Контуре();

}

void RGB_CheckedChanged(object sender, EventArgs e)

{

if(RGB.Checked)

Пронађи_Контуре();

}

void Црно_Бело_CheckedChanged(object sender, EventArgs e)

{

if(Црно_Бело.Checked)

Пронађи_Контуре();

}

void Нијансе_Сиве_CheckedChanged(object sender, EventArgs e)

{

if(Нијансе_Сиве.Checked)

Пронађи_Контуре();

}

void Доња_Граница_Осветљења_ValueChanged(object sender, EventArgs e)

{

Пронађи_Контуре();

}

void Горња_Граница_ОсветљењаValueChanged(object sender, EventArgs e)

{

Пронађи_Контуре();

}

#endregion

}

}

/************************************************************************

* Program Licence : *

* *

* Copyright 2015 , Perić Željko *

* (periczeljkosmederevo@yahoo.com) *

* *

* According to it's main purpose , this program is licensed *

* under the therms of 'Free Software' licence agreement. *

* *

* If You do not know what those therms applies to *

* please read explanation at the following link : *

* (http://www.gnu.org/philosophy/free-sw.html.en) *

* *

* Since it is Free Software this program has no warranty of any kind. *

************************************************************************

* Ethical Notice : *

* *

* It is not ethical to change program code signed by it's author *

* and then to redistribute it under the same author name , *

* especially if it is incorrect. *

* *

* It is recommended that if You make improvement in program code , *

* to make remarks of it and then to sign it with Your own name , *

* for further redistribution as new major version of program. *

* *

* Author name and references of old program code version should be *

* kept , for tracking history of program development. *

* *

* For any further information please contact code author at his email. *

************************************************************************/


/************************************

* List Of Revisions *

************************************

* *

* *

* *

************************************/