Детекција контура објеката на слици
/******************************************************************************************************
* Назив програма : Детекција контура објеката на слици *
* Верзија програма : 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 *
************************************
* *
* *
* *
************************************/