نوشته‌ها

کار با Thread ها در زبان سی شارپ – آشنایی با فضای نام System.Threading و کلاس Thread

تا اینجا متوجه شدیم که چگونه می توان با کمک Delegate ها کدها را در یک Thread جداگانه و به صورت Asynchrnonous اجرا کرد. در ادامه مباحث مرتبط با برنامه نویسی Asynchronous به سراغ فضای نام System.Threading می رویم. این فضای نام شامل یکسری کلاس است که روند نوشتن برنامه Multi-Threaded را آسان می کند. کلاس های زیادی در این فضای نام وجود دارد که هر یک استفاده خاص خودش را دارد. در زیر با توضیح اولیه برخی از کلاس های این فضای نام آشنا می شویم:

  1. Interlocked: از این کلاس برای اجرای عملیات های atomic یا Atomic Operations بر روی متغیرهایی که در بین چندین Thread به اشتراک گذاشته شدند استفاده می شود.
  2. Monitor: از این کلاس برای پیاده سازی Synchronization بر روی اشیاء ای که Thread به آن دسترسی دارند استفاده می شود. در سی شارپ کلمه کلیدی lock در پشت زمینه از مکانیزم Monitor برای Synchronization استفاده می کنید که در بخش های بعدی با این تکنیک بیشتر آشنا می شویم.
  3. Mutex: از این کلاس برای اعمال Synchronization بین AppDomain ها استفاده می شود.
  4. ParameterizedThreadStart: این delegate به thread ها این اجازه را می دهد تا متدهایی پارامتر ورودی دارند را فراخوانی کند.
  5. Semaphor: از این کلاس برای محدود کردن تعداد Thread هایی که می توانند به یک Resource دسترسی داشته باشند استفاده می شود.
  6. Thread: به ازای هر Thread ایجاد شده برای برنامه باید یک کلاس از نوع Thread ایجاد کرد. در حقیقت کلاس Thread نقش اصلی را در ایجاد و استفاده از Thread ها دارد.
  7. ThreadPool: بوسیله این کلاس می توان به Thread-Pool مدیریت شده توسط خود CLR دسترسی داشت.
  8. ThreadPriority: بوسیله این enum می توان درجه اهمیت یک Thread را مشخص می کند.
  9. ThreadStart: از این delegate برای مشخص کردن متدی که در Thread باید اجرا شود استفاده می شود. این delegate بر خلاف ParameterizedThreadStart پارامتری قبول نمیکند.
  10. ThreadState: بوسیله این enum می توان وضعیت جاری یک thread را مشخص کرد.
  11. Timer: بوسیله کلاس Timer می توان مکانیزمی پیاده کرد که کدهای مورد نظر در بازه های زمانی خاص، مثلاً هر ۵ ثانیه یکبار و در یک Thread مجزا اجرا شوند.
  12. TimerCallBack: از این delegate برای مشخص کردن کدی که داخل timer باید اجرا شود استفاده می شود.

کلاس System.Threading.Thread

کلاس Thread اصلی ترین کلاس موجود در فضای نام System.Threading است. از یک کلاس برای دسترسی به Thread هایی که در روند اجرای یک AppDomain ایجاد شده اند استفاده می شود. همچنین بوسیله این کلاس می تواند Thread های جدید را نیز ایجاد کرد. کلاس Thread شامل یکسری متد و خصوصیت است که در این قسمت می خواهیم با آن ها آشنا شویم. ابتدا به سراغ خصوصیت CurrentThread که یک خصوصیت static در کلاس Thread است می رویم. بوسیله این خصوصیت می توان اطلاعات Thread جاری را بدست آورد. برای مثال در صورتی که در متد Main از این خصوصیت استفاده شود می توان به اطلاعات مربوط به Thread اصلی برنامه دسترسی داشت یا اگر برای یک متد Thread جداگانه ای ایجاد شود، در صورت استفاده از این خصوصیت در بدنه متد به اطلاعات Thread ایجاد شده دسترسی خواهیم داشت. در ابتدا با یک مثال می خواهیم اطلاعات Thread اصلی برنامه را بدست آوریم:

var primaryThread = Thread.CurrentThread;
primaryThread.Name = "PrimaryThread";
Console.WriteLine("Thread Name: {0}", primaryThread.Name);
Console.WriteLine("Thread AppDomain: {0}", Thread.GetDomain().FriendlyName);
Console.WriteLine("Thread Context Id: {0}", Thread.CurrentContext.ContextID);
Console.WriteLine("Thread Statred: {0}", primaryThread.IsAlive);
Console.WriteLine("Thread Priority: {0}", primaryThread.Priority);
Console.WriteLine("Thread State: {0}", primaryThread.ThreadState);

دقت کنید در ابتدا با به Thread یک نام دادیم. در صورتی که نامی برای Thread انتخاب نشود خصوصیت Name مقدار خالی بر میگرداند. مهمترین مزیت تخصیص نام برای Thread راحت تر کردن امکان debug کردن کد است. در Visual Studio پنجره ای وجود دارد به نام Threads که می توانید در زمان اجرای برنامه از طریق منوی Debug->Windows->Threads به آن دسترسی داشته باشید. در تصویر زیر نمونه ای از این پنجره را در زمان اجرا مشاهده می کنید:

آشنایی با فضای نام System.Threading و کلاس Thread

اما بریم سراغ موضوع اصلی، یعنی ایجاد Thread و اجرای آن. در ابتدا در مورد مراحل ایجاد یک Thread صحبت کنیم، معمولاً برای ایجاد یک Thread مراحل زیر را باید انجام دهیم:

  1. در ابتدا باید متدی ایجاد کنیم که وظیفه آن انجام کاری است که قرار است در یک Thread جداگانه انجام شود.
  2. در مرحله بعد باید یکی از delegate های ParameterizedThreadStart برای متدهایی که پارامتر ورودی دارند یا ThreadStart برای متدهای بدون پارامتر را انتخاب کرده و یک شئ از آن ایجاد کنیم که به عنوان سازنده متد مورد نظر به آن پاس داده می شود.
  3. از روی کلاس Thread یک شئ جدید ایجاد کرده و به عنوان سازنده شئ ای که از روی delegate های گفته شده در مرحله ۲ ساختیم را به آن ارسال کنیم.
  4. اطلاعات و تنظیمات اولیه مورد نظر برای Thread مانند Name یا Priority را برای آن ست کنیم.
  5. متد Start را در کلاس Thread را برای شروع کار Thread فراخوانی کنیم. با این کار متدی که در مرحله ۲ مشخص کردیم در یک Thread جداگانه اجرا می شود.

دقت کنید در مرحله ۲ می بایست بر اساس signature متدی که قصد اجرای آن در thread جداگانه را داریم، delegate مناسب انتخاب شود. همچنین ParameterizedThreadStart پارامتری که به عنوان ورودی قبول می کند از نوع Object است، یعنی اگر می خواهید چندین پارامتر به آن ارسال کنید می بایست حتماً یک کلاس یا struct ایجاد کرده و آن را به عنوان ورودی به کلاس Start ارسال کنید. با یک مثال ساده که از ThreadStart برای اجرای Thread استفاده می کند شروع می کنیم:

static void Main(string[] args)
{
    ThreadStart threadStart = new ThreadStart(PrintNumbers);
    Thread thread = new Thread(threadStart);
    thread.Name = "PrintNumbersThread";
    thread.Start();
    while (thread.IsAlive)
    {
        Console.WriteLine("Running in primary thread...");
        Thread.Sleep(2000);
    }
    Console.WriteLine("All done.");
    Console.ReadKey();
}

public static void PrintNumbers()
{
    for (int counter = 0; counter < 10; counter++)
    {
        Console.WriteLine("Running from thread: {0}", counter + 1);
        Thread.Sleep(500);
    }
}

در ابتدا متدی تعریف کردیم با نام PrintNumbers که قرار است در یک Thread مجزا اجرا شود. همانطور که مشاهده می کنید این متد نه پارامتر ورودی دارد و نه مقدار خروجی، پس از ThreadStart استفاده می کنیم. بعد از ایجاد شئ از روی ThreadStart و ایجاد Thread، نام Thread را مشخص کرده و متد Start را فراخوانی کردیم. به حلقه while ایجاد شده دقت کنید، در این حلقه بوسیله خصوصیت IsAlive گفتیم تا زمانی که Thread ایجاد شده در حال اجرا است کد داخل while اجرا شود. همچنین بوسیله متد Sleep در متد Main و متد PrintNumbers در عملیات اجرا برای Thread های مربوط به متد تاخیر ایجاد کردیم. بعد اجرای کد بالا خروجی زیر نمایش داده می شود:

Running in primary thread...
Running from thread: 1
Running from thread: 2
Running from thread: 3
Running from thread: 4
Running in primary thread...
Running from thread: 5
Running from thread: 6
Running from thread: 7
Running from thread: 8
Running in primary thread...
Running from thread: 9
Running from thread: 10
All done.

در قدم بعدی فرض کنید که قصد داریم بازه اعدادی که قرار است در خروجی چاپ شود را به عنوان پارامتر ورودی مشخص کنیم، در اینجا ابتدا یک کلاس به صورت زیر تعریف می کنیم:

public class PrintNumberParameters
{
    public int Start { get; set; }
    public int Finish { get; set; }
}

در قدم بعدی کلاس PrintNumbers را به صورت زیر تغییر می دهیم:

public static void PrintNumbers(object data)
{
    PrintNumberParameters parameters = (PrintNumberParameters) data;
    for (int counter = parameters.Start; counter < parameters.Finish; counter++)
    {
        Console.WriteLine("Running from thread: {0}", counter);
        Thread.Sleep(500);
    }
}

همانطور که مشاهده می کنید، پارامتر ورودی PrintNumbers از نوع object است و در بدنه ورودی را به کلاس PrintNumberParameters تبدیل کرده و از آن استفاده کردیم. در مرحله بعد متد Main را باید تغییر داده و به جای ThreadStart از ParameterizedThreadStart استفاده کنیم، همچنین به عنوان پارامتر ورودی برای متد Start شئ ای از PrintNumberParameters ایجاد کرده و با عنوان پارامتر به آن ارسال می کنیم:

ParameterizedThreadStart threadStart = new ParameterizedThreadStart(PrintNumbers);
Thread thread = new Thread(threadStart);
thread.Name = "PrintNumbersThread";
thread.Start(new PrintNumberParameters() {Start = 5, Finish = 13});
while (thread.IsAlive)
{
    Console.WriteLine("Running in primary thread...");
    Thread.Sleep(2000);
}
Console.WriteLine("All done.");
Console.ReadKey();

با اعمال تغییرات ذکر شده و اجرای کد، اعداد بر اساس بازه مشخص شده در خروجی چاپ می شوند. در این قسمت از مطلب مربوط به Thread ها با نحوه ایجاد و استفاده از Thread ها آشنا شدیم. در قسمت های بعدی به مباحث دیگری در مورد Thread ها خواهیم پرداخت.

منبع


قسمت اول آموزش-برنامه نویسی Asynchronous – آشنایی با Process ها، Thread ها و AppDomain ها

قسمت دوم آموزش- آشنایی با ماهیت Asynchronous در Delegate ها

قسمت سوم آموزش-آشنایی با فضای نام System.Threading و کلاس Thread

قسمت چهارم آموزش- آشنایی با Thread های Foreground و Background در دات نت

قسمت پنجم آموزش- آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

قسمت ششم آموزش- آشنایی با کلاس Timer در زبان سی شارپ

قسمت هفتم آموزش-آشنایی با CLR ThreadPool در دات نت

قسمت هشتم آموزش- مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

قسمت نهم آموزش- برنامه نویسی Parallel:آشنایی با کلاس Task در سی شارپ

قسمت دهم آموزش-برنامه نویسی Parallel در سی شارپ :: متوقف کردن Task ها در سی شارپ – کلاس CancellationToken

قسمت یازدهم آموزش- برنامه نویسی Parallel در سی شارپ :: کوئری های Parallel در LINQ

قسمت دوازدهم آموزش- آشنایی با کلمات کلیدی async و await در زبان سی شارپ

قسمت سیزدهم آموزش- استفاده از متد WhenAll برای اجرای چندین Task به صورت همزمان در سی شارپ

 

مرحله ۳: ردیابی در امتداد لبه ها

گام بعدی در واقع این است که در امتداد لبه ها بر اساس نقاط قوت و جهت های لبه که قبلا محاسبه شده است ردیابی شود. هر پیکسل از طریق استفاده از دو تودرتو برای حلقه ها چرخه می زند. اگر پیکسل فعلی دارای قدرت شیب بیشتر از مقدار upperThreshold تعریف شده باشد، یک سوئیچ اجرا می شود. این سوئیچ توسط جهت لبه پیکسل فعلی تعیین می شود. این ردیف و ستون، پیکسل ممکن بعدی را در این جهت ذخیره می کند و سپس جهت لبه و استحکام شیب آن پیکسل را آزمایش می کند. اگر آن همان جهت لبه و  قدرت گرادیان بزرگتر از lowerThreshold را دارد، آن پیکسل به سفید و پیکسل بعدی در امتداد آن لبه آزمایش می شود. به این ترتیب هر لبه قابل توجه تیز تشخیص داده شده و به سفید تنظیم می شود در حالیکه تمام پیکسل های دیگر به سیاه تنظیم می شود.

 

#include "stdafx.h"
#include "tripod.h"
#include "tripodDlg.h"

#include "LVServerDefs.h"
#include "math.h"
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace std;

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg dialog

CTripodDlg::CTripodDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CTripodDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CTripodDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	//////////////// Set destination BMP to NULL first 
	m_destinationBitmapInfoHeader = NULL;

}

////////////////////// Additional generic functions

static unsigned PixelBytes(int w, int bpp)
{
    return (w * bpp + 7) / 8;
}

static unsigned DibRowSize(int w, int bpp)
{
    return (w * bpp + 31) / 32 * 4;
}

static unsigned DibRowSize(LPBITMAPINFOHEADER pbi)
{
    return DibRowSize(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibRowPadding(int w, int bpp)
{
    return DibRowSize(w, bpp) - PixelBytes(w, bpp);
}

static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
    return DibRowPadding(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibImageSize(int w, int h, int bpp)
{
    return h * DibRowSize(w, bpp);
}

static size_t DibSize(int w, int h, int bpp)
{
    return sizeof (BITMAPINFOHEADER) + DibImageSize(w, h, bpp);
}

/////////////////////// end of generic functions


void CTripodDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTripodDlg)
	DDX_Control(pDX, IDC_PROCESSEDVIEW, m_cVideoProcessedView);
	DDX_Control(pDX, IDC_UNPROCESSEDVIEW, m_cVideoUnprocessedView);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTripodDlg, CDialog)
	//{{AFX_MSG_MAP(CTripodDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDEXIT, OnExit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg message handlers

BOOL CTripodDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here

	// For Unprocessed view videoportal (top one)
	char sRegUnprocessedView[] = "HKEY_LOCAL_MACHINE\\Software\\UnprocessedView";
	m_cVideoUnprocessedView.PrepareControl("UnprocessedView", sRegUnprocessedView, 0 );	
	m_cVideoUnprocessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoUnprocessedView.ConnectCamera2();
	m_cVideoUnprocessedView.SetEnablePreview(TRUE);

	// For binary view videoportal (bottom one)
	char sRegProcessedView[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedView";
	m_cVideoProcessedView.PrepareControl("ProcessedView", sRegProcessedView, 0 );	
	m_cVideoProcessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoProcessedView.ConnectCamera2();
	m_cVideoProcessedView.SetEnablePreview(TRUE);

	// Initialize the size of binary videoportal
	m_cVideoProcessedView.SetPreviewMaxHeight(240);
	m_cVideoProcessedView.SetPreviewMaxWidth(320);

	// Uncomment if you wish to fix the live videoportal's size
	// m_cVideoUnprocessedView.SetPreviewMaxHeight(240);
	// m_cVideoUnprocessedView.SetPreviewMaxWidth(320);

	// Find the screen coodinates of the binary videoportal
	m_cVideoProcessedView.GetWindowRect(m_rectForProcessedView);
	ScreenToClient(m_rectForProcessedView);
	allocateDib(CSize(320, 240));

	// Start grabbing frame data for Procssed videoportal (bottom one)
	m_cVideoProcessedView.StartVideoHook(0);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTripodDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTripodDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTripodDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CTripodDlg::OnExit() 
{
	// TODO: Add your control notification handler code here

	// Kill live view videoportal (top one)
	m_cVideoUnprocessedView.StopVideoHook(0);
    m_cVideoUnprocessedView.DisconnectCamera();	
	
	// Kill binary view videoportal (bottom one)
	m_cVideoProcessedView.StopVideoHook(0);
    m_cVideoProcessedView.DisconnectCamera();	

	// Kill program
	DestroyWindow();	

	

}

BEGIN_EVENTSINK_MAP(CTripodDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CTripodDlg)
	ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEW, 1 /* PortalNotification */, OnPortalNotificationProcessedview, VTS_I4 VTS_I4 VTS_I4 VTS_I4)
	//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CTripodDlg::OnPortalNotificationProcessedview(long lMsg, long lParam1, long lParam2, long lParam3) 
{
	// TODO: Add your control notification handler code here
	
	// This function is called at the camera's frame rate
    
#define NOTIFICATIONMSG_VIDEOHOOK	۱۰

	// Declare some useful variables
	// QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too 
	
	LPBITMAPINFOHEADER lpBitmapInfoHeader; // Frame's info header contains info like width and height
	LPBYTE lpBitmapPixelData; // This pointer-to-long will point to the start of the frame's pixel data
    unsigned long lTimeStamp; // Time when frame was grabbed

	switch(lMsg) {
		case NOTIFICATIONMSG_VIDEOHOOK:
			{
				lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1; 
				lpBitmapPixelData = (LPBYTE) lParam2;
				lTimeStamp = (unsigned long) lParam3;

				grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData);
				doMyImageProcessing(lpBitmapInfoHeader); // Place where you'd add your image processing code
				displayMyResults(lpBitmapInfoHeader);

			}
			break;

		default:
			break;
	}	
}

void CTripodDlg::allocateDib(CSize sz)
{
	// Purpose: allocate information for a device independent bitmap (DIB)
	// Called from OnInitVideo

	if(m_destinationBitmapInfoHeader) {
		free(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader = NULL;
	}

	if(sz.cx | sz.cy) {
		m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapInfoHeader->biWidth = sz.cx;
		m_destinationBitmapInfoHeader->biHeight = sz.cy;
		m_destinationBitmapInfoHeader->biPlanes = 1;
		m_destinationBitmapInfoHeader->biBitCount = 24;
		m_destinationBitmapInfoHeader->biCompression = 0;
		m_destinationBitmapInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapInfoHeader->biXPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biYPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biClrImportant = 0;
		m_destinationBitmapInfoHeader->biClrUsed = 0;
	}
}

void CTripodDlg::displayMyResults(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// displayMyResults: Displays results of doMyImageProcessing() in the videoport
	// Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size

	CDC				*pDC;	// Device context to display bitmap data
	
	pDC = GetDC();	
	int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC->GetSafeHdc(),
		m_rectForProcessedView.left,				// videoportal left-most coordinate
		m_rectForProcessedView.top,					// videoportal top-most coordinate
		m_rectForProcessedView.Width(),				// videoportal width
		m_rectForProcessedView.Height(),			// videoportal height
		۰,											// Row position to display bitmap in videoportal
		۰,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader->biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader->biHeight,			// m_destinationBmp's number of rows
		m_destinationBmp,							// The bitmap to display; use the one resulting from doMyImageProcessing
		(BITMAPINFO*)m_destinationBitmapInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc
		DIB_RGB_COLORS,								// Use default 24-bit color table
		SRCCOPY										// Just display
	);
 
	SetStretchBltMode(pDC->GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);

	// Note: 04/24/02 - Added the following:
	// Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed

	// Recall OnPortalNotificationProcessedview, which gets called everytime
	// a frame of data arrives, performs 3 steps:
	// (۱) grayScaleTheFrameData - which mallocs m_destinationBmp
	// (۲) doMyImageProcesing
	// (۳) displayMyResults - which we're in now
	// Since we're finished with the memory we malloc'ed for m_destinationBmp
	// we should free it: 
	
	free(m_destinationBmp);

	// End of adds
}

void CTripodDlg::grayScaleTheFrameData(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData)
{

	// grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview
	// Task: Read current frame pixel data and computes a grayscale version

	unsigned int	W, H;			  // Width and Height of current frame [pixels]
	BYTE            *sourceBmp;		  // Pointer to current frame of data
	unsigned int    row, col;
	unsigned long   i;
	BYTE			grayValue;

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows

	// Store pixel data in row-column vector format
	// Recall that each pixel requires 3 bytes (red, blue and green bytes)
	// m_destinationBmp is a protected member and declared in binarizeDlg.h

	m_destinationBmp = (BYTE*)malloc(H*3*W*sizeof(BYTE));

	// Point to the current frame's pixel data
	sourceBmp = lpThisBitmapPixelData;

	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {

			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
        
			// The source pixel has a blue, green andred value:
			blueValue  = *(sourceBmp + i);
			greenValue = *(sourceBmp + i + 1);
			redValue   = *(sourceBmp + i + 2);

			// A standard equation for computing a grayscale value based on RGB values
			grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue);

			// The destination BMP will be a grayscale version of the source BMP
			*(m_destinationBmp + i)     = grayValue;
			*(m_destinationBmp + i + 1) = grayValue;
			*(m_destinationBmp + i + 2) = grayValue;
			
		}
	}
}


void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// doMyImageProcessing:  This is where you'd write your own image processing code
	// Task: Read a pixel's grayscale value and process accordingly

	unsigned int	W, H;			// Width and Height of current frame [pixels]
	unsigned int    row, col;		// Pixel's row and col positions
	unsigned long   i;				// Dummy variable for row-column vector
	int	    upperThreshold = 60;	// Gradient strength nessicary to start edge
	int		lowerThreshold = 30;	// Minimum gradient strength to continue edge
	unsigned long iOffset;			// Variable to offset row-column vector during sobel mask
	int rowOffset;					// Row offset from the current pixel
	int colOffset;					// Col offset from the current pixel
	int rowTotal = 0;				// Row position of offset pixel
	int colTotal = 0;				// Col position of offset pixel
	int Gx;							// Sum of Sobel mask products values in the x direction
	int Gy;							// Sum of Sobel mask products values in the y direction
	float thisAngle;				// Gradient direction based on Gx and Gy
	int newAngle;					// Approximation of the gradient direction
	bool edgeEnd;					// Stores whether or not the edge is at the edge of the possible image
	int GxMask[3][3];				// Sobel mask in the x direction
	int GyMask[3][3];				// Sobel mask in the y direction
	int newPixel;					// Sum pixel values for gaussian
	int gaussianMask[5][5];			// Gaussian mask

	W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
	
	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {
			edgeDir[row][col] = 0;
		}
	}

	/* Declare Sobel masks */
	GxMask[0][0] = -1; GxMask[0][1] = 0; GxMask[0][2] = 1;
	GxMask[1][0] = -2; GxMask[1][1] = 0; GxMask[1][2] = 2;
	GxMask[2][0] = -1; GxMask[2][1] = 0; GxMask[2][2] = 1;
	
	GyMask[0][0] =  1; GyMask[0][1] =  2; GyMask[0][2] =  1;
	GyMask[1][0] =  0; GyMask[1][1] =  0; GyMask[1][2] =  0;
	GyMask[2][0] = -1; GyMask[2][1] = -2; GyMask[2][2] = -1;

	/* Declare Gaussian mask */
	gaussianMask[0][0] = 2;		gaussianMask[0][1] = 4;		gaussianMask[0][2] = 5;		gaussianMask[0][3] = 4;		gaussianMask[0][4] = 2;	
	gaussianMask[1][0] = 4;		gaussianMask[1][1] = 9;		gaussianMask[1][2] = 12;	gaussianMask[1][3] = 9;		gaussianMask[1][4] = 4;	
	gaussianMask[2][0] = 5;		gaussianMask[2][1] = 12;	gaussianMask[2][2] = 15;	gaussianMask[2][3] = 12;	gaussianMask[2][4] = 2;	
	gaussianMask[3][0] = 4;		gaussianMask[3][1] = 9;		gaussianMask[3][2] = 12;	gaussianMask[3][3] = 9;		gaussianMask[3][4] = 4;	
	gaussianMask[4][0] = 2;		gaussianMask[4][1] = 4;		gaussianMask[4][2] = 5;		gaussianMask[4][3] = 4;		gaussianMask[4][4] = 2;	
	

	/* Gaussian Blur */
	for (row = 2; row < H-2; row++) {
		for (col = 2; col < W-2; col++) {
			newPixel = 0;
			for (rowOffset=-2; rowOffset<=2; rowOffset++) {
				for (colOffset=-2; colOffset<=2; colOffset++) {
					rowTotal = row + rowOffset;
					colTotal = col + colOffset;
					iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
					newPixel += (*(m_destinationBmp + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset];
				}
			}
			i = (unsigned long)(row*3*W + col*3);
			*(m_destinationBmp + i) = newPixel / 159;
		}
	}

	/* Determine edge directions and gradient strengths */
	for (row = 1; row < H-1; row++) {
		for (col = 1; col < W-1; col++) {
			i = (unsigned long)(row*3*W + 3*col);
			Gx = 0;
			Gy = 0;
			/* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */
			for (rowOffset=-1; rowOffset<=1; rowOffset++) {
				for (colOffset=-1; colOffset<=1; colOffset++) {
					rowTotal = row + rowOffset;
					colTotal = col + colOffset;
					iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
					Gx = Gx + (*(m_destinationBmp + iOffset) * GxMask[rowOffset + 1][colOffset + 1]);
					Gy = Gy + (*(m_destinationBmp + iOffset) * GyMask[rowOffset + 1][colOffset + 1]);
				}
			}

			gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0));	// Calculate gradient strength			
			thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0;		// Calculate actual direction of edge
			
			/* Convert actual edge direction to approximate value */
			if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) )
				newAngle = 0;
			if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) )
				newAngle = 45;
			if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) )
				newAngle = 90;
			if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) )
				newAngle = 135;
				
			edgeDir[row][col] = newAngle;		// Store the approximate edge direction of each pixel in one array
		}
	}

	/* Trace along all the edges in the image */
	for (row = 1; row < H - 1; row++) {
		for (col = 1; col < W - 1; col++) {
			edgeEnd = false;
			if (gradient[row][col] > upperThreshold) {		// Check to see if current pixel has a high enough gradient strength to be part of an edge
				/* Switch based on current pixel's edge direction */
				switch (edgeDir[row][col]){		
					case 0:
						findEdge(0, 1, row, col, 0, lowerThreshold);
						break;
					case 45:
						findEdge(1, 1, row, col, 45, lowerThreshold);
						break;
					case 90:
						findEdge(1, 0, row, col, 90, lowerThreshold);
						break;
					case 135:
						findEdge(1, -1, row, col, 135, lowerThreshold);
						break;
					default :
						i = (unsigned long)(row*3*W + 3*col);
						*(m_destinationBmp + i) = 
						*(m_destinationBmp + i + 1) = 
						*(m_destinationBmp + i + 2) = 0;
						break;
					}
				}
			else {
				i = (unsigned long)(row*3*W + 3*col);
					*(m_destinationBmp + i) = 
					*(m_destinationBmp + i + 1) = 
					*(m_destinationBmp + i + 2) = 0;
			}	
		}
	}
	
	/* Suppress any pixels not changed by the edge tracing */
	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {	
			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
			// If a pixel's grayValue is not black or white make it black
			if( ((*(m_destinationBmp + i) != 255) && (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) && (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) && (*(m_destinationBmp + i + 2) != 0)) ) 
				*(m_destinationBmp + i) = 
				*(m_destinationBmp + i + 1) = 
				*(m_destinationBmp + i + 2) = 0; // Make pixel black
		}
	}

	/* Non-maximum Suppression */
	for (row = 1; row < H - 1; row++) {
		for (col = 1; col < W - 1; col++) {
			i = (unsigned long)(row*3*W + 3*col);
			if (*(m_destinationBmp + i) == 255) {		// Check to see if current pixel is an edge
				/* Switch based on current pixel's edge direction */
				switch (edgeDir[row][col]) {		
					case 0:
						suppressNonMax( 1, 0, row, col, 0, lowerThreshold);
						break;
					case 45:
						suppressNonMax( 1, -1, row, col, 45, lowerThreshold);
						break;
					case 90:
						suppressNonMax( 0, 1, row, col, 90, lowerThreshold);
						break;
					case 135:
						suppressNonMax( 1, 1, row, col, 135, lowerThreshold);
						break;
					default :
						break;
				}
			}	
		}
	}
	
}

void CTripodDlg::findEdge(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
	int W = 320;
	int H = 240;
	int newRow;
	int newCol;
	unsigned long i;
	bool edgeEnd = false;

	/* Find the row and column values for the next possible pixel on the edge */
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
		
	/* Determine edge directions and gradient strengths */
	while ( (edgeDir[newRow][newCol]==dir) && !edgeEnd && (gradient[newRow][newCol] > lowerThreshold) ) {
		/* Set the new pixel as white to show it is an edge */
		i = (unsigned long)(newRow*3*W + 3*newCol);
		*(m_destinationBmp + i) =
		*(m_destinationBmp + i + 1) =
		*(m_destinationBmp + i + 2) = 255;
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
	}	
}

void CTripodDlg::suppressNonMax(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
	int W = 320;
	int H = 240;
	int newRow = 0;
	int newCol = 0;
	unsigned long i;
	bool edgeEnd = false;
	float nonMax[320][3];			// Temporarily stores gradients and positions of pixels in parallel edges
	int pixelCount = 0;					// Stores the number of pixels in parallel edges
	int count;						// A for loop counter
	int max[3];						// Maximum point in a wide edge
	
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	/* Find non-maximum parallel edges tracing up */
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Find non-maximum parallel edges tracing down */
	edgeEnd = false;
	colShift *= -1;
	rowShift *= -1;
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;	
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Suppress non-maximum edges */
	max[0] = 0;
	max[1] = 0;
	max[2] = 0;
	for (count = 0; count < pixelCount; count++) {
		if (nonMax[count][2] > max[2]) {
			max[0] = nonMax[count][0];
			max[1] = nonMax[count][1];
			max[2] = nonMax[count][2];
		}
	}
	for (count = 0; count < pixelCount; count++) {
		i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
		*(m_destinationBmp + i) = 
		*(m_destinationBmp + i + 1) = 
		*(m_destinationBmp + i + 2) = 0;
	}
}

الگوریتم Canny در سی پلاس پلاس قسمت ۱
الگوریتم Canny در سی پلاس پلاس قسمت ۲
الگوریتم Canny در سی پلاس پلاس قسمت ۳
الگوریتم Canny در سی پلاس پلاس قسمت ۴

الگوریتم Canny

لبه یاب کنی توسط جان اف کنی در سال ۱۹۸۶ ایجاد شد و هنوز یک لبه یاب استاندارد و با دقت و کیفیت بالا میباشد.الگوریتم لبه یابی کنی یکی از بهترین لبه یابها تا به امروز است. در ادامه روش کار این الگوریتم و هم چنین کد الگوریتم Canny در C را بررسی خواهیم کرد. این الگوریتم لبه یابی از سه بخش اصلی زیر تشکیل شده است:

  • تضعیف نویز
  • پیدا کردن نقاطی که بتوان آنها را به عنوان لبه در نظر گرفت
  • جذب نقاطی که احتمال لبه بودن آنها کم است

 

معیارهایی که در لبه یاب کنی مطرح می باشد:
۱ -پایین آوردن نرخ خطا- یعنی تا حد امکان هیچ لبه ای در تصویر نباید گم شود و هم چنین هیچ چیزی که لبه نیست نباید به جای لبه فرض شود. لبه هان پیدا شده تا حد ممکن به لبه ها اصلی
نزدیک باشند.

۲ -لبه در مکان واقعی خود باشد- یعنی تا حد ممکن لبه ها کمترین فاصله را با مکان واقعی خود داشته باشند.
۳ -بران هر لبه فقط یک پاسخ داشته باشیم.

۴ -لبه ها کمترین ضخامت را داشته باشند- (در صورت امکان یک پیکسل).
لبه یاب کنی بخاطر توانایی در تولید لبه های نازک تا حد یک ییکسل برای لبه های پیوسته معروف شده است. این لبه یاب شامل چهار مرحله و چهار ورودی زیر است:
یک تصویر ورودی
یک پارامتر به نام سیگما جهت مقدار نرم کنندگی تصویر
یک حد آستانه بالا (Th)
یک حد آستانه پایین (Tl)

 

مراحل الگوریتم Canny:

۱- در ابتدا باید تصویر رنگی را به جهت لبه یابی بهتر به یک تصویر سطح خاکسترن تبدیب کرد.

۲- نویز را از تصویر دریافتی حذف کرد. بدلیل اینکه فیلتر گاوسین از یک ماسک ساده برای حذف نویز استفاده می کند لبه یاب کنی در مرحله اول برای حذف نویز آن را بکار می گیرد.

۳- در یک تصویر سطح خاکستر جایی را که بیشترین تغییرات را داشته باشند به عنوان لبه در نظر گرفته می شوند و این مکانها با گرفتن گرادیان تصویر با استفاده عملگر سوبل بدست می آیند. سپس لبه های مات یافت شده به لبه های تیزتر تبدیل می شوند.

۴- برخی از لبه های کشف شده واقعا لبه نیستند و در واقع نویز هستند که باید آنها توسط حد آستانه هیسترزیس فیلتر شوند.هیسترزیس از دو حد آستانه بالاتر (Th) و حد آستانه پایین تر (Tl) استفاده کرده و کنی پیشنهاد می کند که نسبت استانه بالا به پایین سه به یک باشد.

 این روش بیشتر به کشف لبه های ضعیف به درستی می پردازد و کمتر فریب نویز را می خورد و از بقیه روش ها بهتر است.

 

الگوریتم Canny    عملکرد الگوریتم Canny

 

 

کد الگوریتم Canny در C :

برنامه زیر یک فایل BMP سیاه و سفید ۸ بیت در هر پیکسل را می خواند و نتیجه را در ‘out.bmp’ ذخیره می کند.با `-lm ‘ کامپایل می شود.

 

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
 
#define MAX_BRIGHTNESS 255
 
// C99 doesn't define M_PI (GNU-C99 does)
#define M_PI 3.14159265358979323846264338327
 
/*
 * Loading part taken from
 * http://www.vbforums.com/showthread.php?t=261522
 * BMP info:
 * http://en.wikipedia.org/wiki/BMP_file_format
 *
 * Note: the magic number has been removed from the bmpfile_header_t
 * structure since it causes alignment problems
 * bmpfile_magic_t should be written/read first
 * followed by the
 * bmpfile_header_t
 * [this avoids compiler-specific alignment pragmas etc.]
 */
 
typedef struct {
 uint8_t magic[2];
} bmpfile_magic_t;
 
typedef struct {
 uint32_t filesz;
 uint16_t creator1;
 uint16_t creator2;
 uint32_t bmp_offset;
} bmpfile_header_t;
 
typedef struct {
 uint32_t header_sz;
 int32_t width;
 int32_t height;
 uint16_t nplanes;
 uint16_t bitspp;
 uint32_t compress_type;
 uint32_t bmp_bytesz;
 int32_t hres;
 int32_t vres;
 uint32_t ncolors;
 uint32_t nimpcolors;
} bitmap_info_header_t;
 
typedef struct {
 uint8_t r;
 uint8_t g;
 uint8_t b;
 uint8_t nothing;
} rgb_t;
 
// Use short int instead `unsigned char' so that we can
// store negative values.
typedef short int pixel_t;
 
pixel_t *load_bmp(const char *filename,
 bitmap_info_header_t *bitmapInfoHeader)
{
 FILE *filePtr = fopen(filename, "rb");
 if (filePtr == NULL) {
 perror("fopen()");
 return NULL;
 }
 
 bmpfile_magic_t mag;
 if (fread(&mag, sizeof(bmpfile_magic_t), 1, filePtr) != 1) {
 fclose(filePtr);
 return NULL;
 }
 
 // verify that this is a bmp file by check bitmap id
 // warning: dereferencing type-punned pointer will break
 // strict-aliasing rules [-Wstrict-aliasing]
 if (*((uint16_t*)mag.magic) != 0x4D42) {
 fprintf(stderr, "Not a BMP file: magic=%c%c\n",
 mag.magic[0], mag.magic[1]);
 fclose(filePtr);
 return NULL;
 }
 
 bmpfile_header_t bitmapFileHeader; // our bitmap file header
 // read the bitmap file header
 if (fread(&bitmapFileHeader, sizeof(bmpfile_header_t),
 ۱, filePtr) != 1) {
 fclose(filePtr);
 return NULL;
 }
 
 // read the bitmap info header
 if (fread(bitmapInfoHeader, sizeof(bitmap_info_header_t),
 ۱, filePtr) != 1) {
 fclose(filePtr);
 return NULL;
 }
 
 if (bitmapInfoHeader->compress_type != 0)
 fprintf(stderr, "Warning, compression is not supported.\n");
 
 // move file point to the beginning of bitmap data
 if (fseek(filePtr, bitmapFileHeader.bmp_offset, SEEK_SET)) {
 fclose(filePtr);
 return NULL;
 }
 
 // allocate enough memory for the bitmap image data
 pixel_t *bitmapImage = malloc(bitmapInfoHeader->bmp_bytesz *
 sizeof(pixel_t));
 
 // verify memory allocation
 if (bitmapImage == NULL) {
 fclose(filePtr);
 return NULL;
 }
 
 // read in the bitmap image data
 size_t pad, count=0;
 unsigned char c;
 pad = 4*ceil(bitmapInfoHeader->bitspp*bitmapInfoHeader->width/32.) - bitmapInfoHeader->width;
 for(size_t i=0; i<bitmapInfoHeader->height; i++){
 for(size_t j=0; j<bitmapInfoHeader->width; j++){
 if (fread(&c, sizeof(unsigned char), 1, filePtr) != 1) {
 fclose(filePtr);
 return NULL;
 }
 bitmapImage[count++] = (pixel_t) c;
 }
 fseek(filePtr, pad, SEEK_CUR);
 }
 
 // If we were using unsigned char as pixel_t, then:
 // fread(bitmapImage, 1, bitmapInfoHeader->bmp_bytesz, filePtr);
 
 // close file and return bitmap image data
 fclose(filePtr);
 return bitmapImage;
}
 
// Return: true on error.
bool save_bmp(const char *filename, const bitmap_info_header_t *bmp_ih,
 const pixel_t *data)
{
 FILE* filePtr = fopen(filename, "wb");
 if (filePtr == NULL)
 return true;
 
 bmpfile_magic_t mag = {{0x42, 0x4d}};
 if (fwrite(&mag, sizeof(bmpfile_magic_t), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 
 const uint32_t offset = sizeof(bmpfile_magic_t) +
 sizeof(bmpfile_header_t) +
 sizeof(bitmap_info_header_t) +
 ((۱U << bmp_ih->bitspp) * 4);
 
 const bmpfile_header_t bmp_fh = {
 .filesz = offset + bmp_ih->bmp_bytesz,
 .creator1 = 0,
 .creator2 = 0,
 .bmp_offset = offset
 };
 
 if (fwrite(&bmp_fh, sizeof(bmpfile_header_t), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 if (fwrite(bmp_ih, sizeof(bitmap_info_header_t), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 
 // Palette
 for (size_t i = 0; i < (1U << bmp_ih->bitspp); i++) {
 const rgb_t color = {(uint8_t)i, (uint8_t)i, (uint8_t)i};
 if (fwrite(&color, sizeof(rgb_t), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 }
 
 // We use int instead of uchar, so we can't write img
 // in 1 call any more.
 // fwrite(data, 1, bmp_ih->bmp_bytesz, filePtr);
 
 // Padding: http://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage
 size_t pad = 4*ceil(bmp_ih->bitspp*bmp_ih->width/32.) - bmp_ih->width;
 unsigned char c;
 for(size_t i=0; i < bmp_ih->height; i++) {
 for(size_t j=0; j < bmp_ih->width; j++) {
 c = (unsigned char) data[j + bmp_ih->width*i];
 if (fwrite(&c, sizeof(char), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 }
 c = 0;
 for(size_t j=0; j<pad; j++)
 if (fwrite(&c, sizeof(char), 1, filePtr) != 1) {
 fclose(filePtr);
 return true;
 }
 }
 
 fclose(filePtr);
 return false;
}
 
// if normalize is true, map pixels to range 0..MAX_BRIGHTNESS
void convolution(const pixel_t *in, pixel_t *out, const float *kernel,
 const int nx, const int ny, const int kn,
 const bool normalize)
{
 assert(kn % 2 == 1);
 assert(nx > kn && ny > kn);
 const int khalf = kn / 2;
 float min = FLT_MAX, max = -FLT_MAX;
 
 if (normalize)
 for (int m = khalf; m < nx - khalf; m++)
 for (int n = khalf; n < ny - khalf; n++) {
 float pixel = 0.0;
 size_t c = 0;
 for (int j = -khalf; j <= khalf; j++)
 for (int i = -khalf; i <= khalf; i++) {
 pixel += in[(n - j) * nx + m - i] * kernel;
 c++;
 }
 if (pixel < min)
 min = pixel;
 if (pixel > max)
 max = pixel;
 }
 
 for (int m = khalf; m < nx - khalf; m++)
 for (int n = khalf; n < ny - khalf; n++) {
 float pixel = 0.0;
 size_t c = 0;
 for (int j = -khalf; j <= khalf; j++)
 for (int i = -khalf; i <= khalf; i++) {
 pixel += in[(n - j) * nx + m - i] * kernel;
 c++;
 }
 
 if (normalize)
 pixel = MAX_BRIGHTNESS * (pixel - min) / (max - min);
 out[n * nx + m] = (pixel_t)pixel;
 }
}
 
/*
 * gaussianFilter:
 * http://www.songho.ca/dsp/cannyedge/cannyedge.html
 * determine size of kernel (odd #)
 * ۰٫۰ <= sigma < 0.5 : 3
 * ۰٫۵ <= sigma < 1.0 : 5
 * ۱٫۰ <= sigma < 1.5 : 7
 * ۱٫۵ <= sigma < 2.0 : 9
 * ۲٫۰ <= sigma < 2.5 : 11
 * ۲٫۵ <= sigma < 3.0 : 13 ...
 * kernelSize = 2 * int(2*sigma) + 3;
 */
void gaussian_filter(const pixel_t *in, pixel_t *out,
 const int nx, const int ny, const float sigma)
{
 const int n = 2 * (int)(2 * sigma) + 3;
 const float mean = (float)floor(n / 2.0);
 float kernel[n * n]; // variable length array
 
 fprintf(stderr, "gaussian_filter: kernel size %d, sigma=%g\n",
 n, sigma);
 size_t c = 0;
 for (int i = 0; i < n; i++)
 for (int j = 0; j < n; j++) {
 kernel = exp(-0.5 * (pow((i - mean) / sigma, 2.0) +
 pow((j - mean) / sigma, 2.0)))
 / (۲ * M_PI * sigma * sigma);
 c++;
 }
 
 convolution(in, out, kernel, nx, ny, n, true);
}
 
/*
 * Links:
 * http://en.wikipedia.org/wiki/Canny_edge_detector
 * http://www.tomgibara.com/computer-vision/CannyEdgeDetector.java
 * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
 * http://www.songho.ca/dsp/cannyedge/cannyedge.html
 *
 * Note: T1 and T2 are lower and upper thresholds.
 */
pixel_t *canny_edge_detection(const pixel_t *in,
 const bitmap_info_header_t *bmp_ih,
 const int tmin, const int tmax,
 const float sigma)
{
 const int nx = bmp_ih->width;
 const int ny = bmp_ih->height;
 
 pixel_t *G = calloc(nx * ny * sizeof(pixel_t), 1);
 pixel_t *after_Gx = calloc(nx * ny * sizeof(pixel_t), 1);
 pixel_t *after_Gy = calloc(nx * ny * sizeof(pixel_t), 1);
 pixel_t *nms = calloc(nx * ny * sizeof(pixel_t), 1);
 pixel_t *out = malloc(bmp_ih->bmp_bytesz * sizeof(pixel_t));
 
 if (G == NULL || after_Gx == NULL || after_Gy == NULL ||
 nms == NULL || out == NULL) {
 fprintf(stderr, "canny_edge_detection:"
 " Failed memory allocation(s).\n");
 exit(1);
 }
 
 gaussian_filter(in, out, nx, ny, sigma);
 
 const float Gx[] = {-1, 0, 1,
 -۲, ۰, ۲,
 -۱, ۰, ۱};
 
 convolution(out, after_Gx, Gx, nx, ny, 3, false);
 
 const float Gy[] = { 1, 2, 1,
 ۰, ۰, ۰,
 -۱,-۲,-۱};
 
 convolution(out, after_Gy, Gy, nx, ny, 3, false);
 
 for (int i = 1; i < nx - 1; i++)
 for (int j = 1; j < ny - 1; j++) {
 const int c = i + nx * j;
 // G = abs(after_Gx) + abs(after_Gy);
 G = (pixel_t)hypot(after_Gx, after_Gy);
 }
 
 // Non-maximum suppression, straightforward implementation.
 for (int i = 1; i < nx - 1; i++)
 for (int j = 1; j < ny - 1; j++) {
 const int c = i + nx * j;
 const int nn = c - nx;
 const int ss = c + nx;
 const int ww = c + 1;
 const int ee = c - 1;
 const int nw = nn + 1;
 const int ne = nn - 1;
 const int sw = ss + 1;
 const int se = ss - 1;
 
 const float dir = (float)(fmod(atan2(after_Gy,
 after_Gx) + M_PI,
 M_PI) / M_PI) * 8;
 
 if (((dir <= 1 || dir > 7) && G > G[ee] &&
 G > G[ww]) || // 0 deg
 ((dir > 1 && dir <= 3) && G > G[nw] &&
 G > G[se]) || // 45 deg
 ((dir > 3 && dir <= 5) && G > G[nn] &&
 G > G[ss]) || // 90 deg
 ((dir > 5 && dir <= 7) && G > G[ne] &&
 G > G[sw])) // 135 deg
 nms = G;
 else
 nms = 0;
 }
 
 // Reuse array
 // used as a stack. nx*ny/2 elements should be enough.
 int *edges = (int*) after_Gy;
 memset(out, 0, sizeof(pixel_t) * nx * ny);
 memset(edges, 0, sizeof(pixel_t) * nx * ny);
 
 // Tracing edges with hysteresis . Non-recursive implementation.
 size_t c = 1;
 for (int j = 1; j < ny - 1; j++)
 for (int i = 1; i < nx - 1; i++) {
 if (nms >= tmax && out == 0) { // trace edges
 out = MAX_BRIGHTNESS;
 int nedges = 1;
 edges[0] = c;
 
 do {
 nedges--;
 const int t = edges[nedges];
 
 int nbs[8]; // neighbours
 nbs[0] = t - nx; // nn
 nbs[1] = t + nx; // ss
 nbs[2] = t + 1; // ww
 nbs[3] = t - 1; // ee
 nbs[4] = nbs[0] + 1; // nw
 nbs[5] = nbs[0] - 1; // ne
 nbs[6] = nbs[1] + 1; // sw
 nbs[7] = nbs[1] - 1; // se
 
 for (int k = 0; k < 8; k++)
 if (nms[nbs[k]] >= tmin && out[nbs[k]] == 0) {
 out[nbs[k]] = MAX_BRIGHTNESS;
 edges[nedges] = nbs[k];
 nedges++;
 }
 } while (nedges > 0);
 }
 c++;
 }
 
 free(after_Gx);
 free(after_Gy);
 free(G);
 free(nms);
 
 return out;
}
 
int main(const int argc, const char ** const argv)
{
 if (argc < 2) {
 printf("Usage: %s image.bmp\n", argv[0]);
 return 1;
 }
 
 static bitmap_info_header_t ih;
 const pixel_t *in_bitmap_data = load_bmp(argv[1], &ih);
 if (in_bitmap_data == NULL) {
 fprintf(stderr, "main: BMP image not loaded.\n");
 return 1;
 }
 
 printf("Info: %d x %d x %d\n", ih.width, ih.height, ih.bitspp);
 
 const pixel_t *out_bitmap_data =
 canny_edge_detection(in_bitmap_data, &ih, 45, 50, 1.0f);
 if (out_bitmap_data == NULL) {
 fprintf(stderr, "main: failed canny_edge_detection.\n");
 return 1;
 }
 
 if (save_bmp("out.bmp", &ih, out_bitmap_data)) {
 fprintf(stderr, "main: BMP image not saved.\n");
 return 1;
 }
 
 free((pixel_t*)in_bitmap_data);
 free((pixel_t*)out_bitmap_data);
 return 0;
}

 

دانلود کد فوق از طریق لینک زیر:

Canny in C

رمز فایل : behsanandish.com

الگوریتم Canny

لبه یاب کنی توسط جان اف کنی در سال ۱۹۸۶ ایجاد شد و هنوز یک لبه یاب استاندارد و با دقت و کیفیت بالا میباشد.الگوریتم لبه یابی کنی یکی از بهترین لبه یابها تا به امروز است. در ادامه روش کار این الگوریتم و هم چنین کد الگوریتم Canny در OpenCV را بررسی خواهیم کرد. این الگوریتم لبه یابی از سه بخش اصلی زیر تشکیل شده:

  • تضعیف نویز
  • پیدا کردن نقاطی که بتوان آنها را به عنوان لبه در نظر گرفت
  • حذب نقاطی که احتمال لبه بودن آنها کم است

 

معیارهایی که در لبه یاب کنی مطرح است:
۱ -پایین آوردن نرخ خطا- یعنی تا حد امکان هیچ لبه ای در تصویر نباید گم شود و هم چنین هیچ چیزی که لبه نیست نباید به جای لبه فرض شود. لبه هان پیدا شده تا حد ممکن به لبه ها اصلی
نزدیک باشند.

۲ -لبه در مکان واقعی خود باشد- یعنی تا حد ممکن لبه ها کمترین فاصله را با مکان واقعی خود داشته باشند.
۳ -بران هر لبه فقط یک پاسخ داشته باشیم.

۴ -لبه ها کمترین ضخامت را داشته باشند- (در صورت امکان یک پیکسل).
لبه یاب کنی بخاطر توانایی در تولید لبه های نازک تا حد یک ییکسل برای لبه های پیوسته معروف شده است. این لبه یاب شامل چهار مرحله و چهار ورودی زیر است:
یک تصویر ورودی
یک پارامتر به نام سیگما جهت مقدار نرم کنندگی تصویر
یک حد آستانه بالا (Th)
یک حد آستانه پایین (Tl)

 

مراحل الگوریتم Canny:

۱- در ابتدا باید تصویر رنگی را به جهت لبه یابی بهتر به یک تصویر سطح خاکسترن تبدیب کرد.

۲- نویز را از تصویر دریافتی حذف کرد. بدلیل اینکه فیلتر گاوسین از یک ماسک ساده برای حذف نویز استفاده می کند لبه یاب کنی در مرحله اول برای حذف نویز آن را بکار میگیرد.

۳- در یک تصویر سطح خاکستر جایی را که بیشترین تغییرات را داشته باشند به عنوان لبه در نظر گرفته می شوند و این مکانها با گرفتن گرادیان تصویر با استفاده عملگر سوبل بدست می آیند. سپس لبه های مات یافت شده به لبه های تیزتر تبدیل می شوند.

۴- برخی از لبه های کشف شده واقعا لبه نیستند و در واقع نویز هستند که باید آنها توسط حد آستانه هیسترزیس فیلتر شوند.هیسترزیس از دو حد آستانه بالاتر (Th) و حد آستانه پایین تر (Tl) استفاده کرده و کنی پیشنهاد می کند که نسبت استانه بالا به پایین سه به یک باشد.

 این روش بیشتر به کشف لبه های ضعیف به درستی می پردازد و کمتر فریب نویز را می خورد و از بقیه روش ها بهتر است.

 

 

الگوریتم Canny    عملکرد الگوریتم Canny

 

کد الگوریتم Canny در OpenCV:

 

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/// Global variables

Mat src, src_gray;
Mat dst, detected_edges;

int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
char* window_name = "Edge Map";

/**
 * @function CannyThreshold
 * @brief Trackbar callback - Canny thresholds input with a ratio 1:3
 */
void CannyThreshold(int, void*)
{
  /// Reduce noise with a kernel 3x3
  blur( src_gray, detected_edges, Size(3,3) );

  /// Canny detector
  Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );

  /// Using Canny's output as a mask, we display our result
  dst = Scalar::all(0);

  src.copyTo( dst, detected_edges);
  imshow( window_name, dst );
 }

/** @function main */
int main( int argc, char** argv )
{
  /// Load an image
  src = imread( argv[1] );

  if( !src.data )
  { return -1; }</pre>
<pre>  /// Create a matrix of the same type and size as src (for dst)
  dst.create( src.size(), src.type() );

  /// Convert the image to grayscale
  cvtColor( src, src_gray, CV_BGR2GRAY );

  /// Create a window
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );

  /// Create a Trackbar for user to enter threshold
  createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );

  /// Show the image
  CannyThreshold(0, 0);

  /// Wait until user exit program by pressing a key
  waitKey(0);

  return 0;
  }

 

 

دانلود کد فوق از طریق لینک زیر:

CannyInOpenCV

رمز فایل : behsanandish.com

 

مسئله فروشنده دوره‌گرد

 اگر فروشنده دوره گرد از نقطه A شروع کند و فواصل بین نقاط مشخص باشد، کوتاه‌تربن مسیر که از تمام نقاط یکبار بازدید می‌کند و به A بازمی‌گردد کدام است؟

مسئله فروشنده دوره گرد (به انگلیسی: Travelling salesman problem، به‌اختصار: TSP) مسئله‌ای مشهور است که ابتدا در سده ۱۸مسائل مربوط به آن توسط ویلیام همیلتون و توماس کرکمن مطرح شد و سپس در دهه ۱۹۳۰ شکل عمومی آن به وسیله ریاضیدانانی مثلکارل منگر از دانشگاه هاروارد و هاسلر ویتنی از دانشگاه پرینستون مورد مطالعه قرار گرفت.

شرح مسئله بدین شکل است:

تعدادی شهر داریم و هزینه رفتن مستقیم از یکی به دیگری را می‌دانیم. مطلوب است کم‌هزینه‌ترین مسیری که از یک شهر شروع شود و از تمامی شهرها دقیقاً یکبار عبور کند و به شهر شروع برگردد.

 

مسئله فروشنده دوره گرد

تعداد جواب‌های شدنی مسئله، برابر است با {\displaystyle {\frac {1}{2}}(n-1)!}{\displaystyle {\frac {1}{2}}(n-1)!} برای n>۲ که n تعداد شهرها می‌باشد. در واقع این عدد برابر است با تعداددورهای همیلتونی در یک گراف کامل با n رأس.

مسئله‌های مرتبط

مسئله فروشنده دوره گرد یا Traveling Salesman Problem (به اختصار TSP)، یکی از مسائل بسیار مهم و پرکاربرد در علوم کامپیوتر و تحقیق در عملیات است.

سه روش کلی برای کد کردن راه حل‌های مسئله TSP ارائه شده‌است که در الگوریتم‌های مختلفی قابل استفاده هستند. راه حل‌های سه گاه عبارتند از:

الف) نمایش جواب به صورت رشته گسسته جایگشتی که در الگوریتم‌های زیر قابل استفاده است: الگوریتم ژنتیک یا Genetic Algorithms (به اختصار GA) شبیه‌سازی تبرید یا Simulated Annealing (به اختصار SA) جستجوی ممنوعه یا Tabu Search (به اختصار TS) جستجوی همسایگی متغیر یا Variable Neighborhood Search (به اختصار VNS) بهینه‌سازی کلونی مورچگان یا Ant Colony Optimization (به اختصار ACO) جستجوی هارمونی یا Harmony Search (به اختصار HS) و سایر الگوریتم‌های بهینه‌سازی گسسته

ب) نمایش جواب به صورت کلیدهای تصادفی یا Random Key که در الگوریتم‌های زیر قابل استفاده است: الگوریتم ژنتیک یا Genetic Algorithms (به اختصار GA) بهینه‌سازی ازدحام ذرات یا Particle Swarm Optimization (به اختصار PSO) الگوریتم رقابت استعماری یا Imperialist Competitive Algorithm (به اختصار ICA) تکامل تفاضلی یا Differential Evolution (به اختصار DE) بهینه‌سازی مبتنی بر جغرافیای زیستی یا Bio-geography Based Optimization (به اختصار BBO) استراتژی‌های تکاملی یا Evolution Strategies (به اختصار ES) برنامه‌ریزی تکاملی یا Evolutionary Programming (به اختصار EP) و سایر الگوریتم‌های بهینه‌سازی پیوسته

پ) نمایش جواب به شکل ماتریس‌های شبیه فرومون که توسط تمامی الگوریتم‌های اشاره شده در مورد (ب) قابل استفاده می‌باشد.

  • مسئله معادل در نظریه گراف به این صورت است که یک گراف وزن‌دار کامل داریم که می‌خواهیم کم‌وزن‌ترین دور همیلتونی را پیدا کنیم.
  • مسئله تنگراه فروشنده دوره‌گرد (به انگلیسی: Bottleneck traveling salesman problem، به‌اختصار: bottleneck TSP) مسئله‌ای بسیار کاربردی است که در یک گراف وزن‌دار کم‌وزن‌ترین دور همیلتونی را می‌خواهد که شامل سنگین‌ترین یال باشد.
  • تعمیم‌یافته مسئله فروشنده دوره‌گرد دارای ایالت‌هایی است که هر کدام حداقل یک شهر دارند و فروشنده باید از هر ایالت دقیقاً از یک شهر عبور کند. این مسئله به «مسئله سیاست‌مدار مسافر» نیز شهرت دارد.

الگوریتم‌ها

مسئله فروشنده دوره گرد جزء مسائل ان‌پی سخت است. راه‌های معمول مقابله با چنین مسائلی عبارتند از:

  • طراحی الگوریتم‌هایی برای پیدا کردن جواب‌های دقیق که استفاده از آن‌ها فقط برای مسائل با اندازه کوچک صورت می‌گیرد.
  • استفاده از الگوریتم‌های مکاشفه‌ای که جواب‌هایی به‌دست می‌دهد که احتمالاً درست هستند.
  • پیدا کردن زیرمسئله‌هایی از مسئله یا به عبارت دیگر تقسیم مسئله به مسئله‌های کوچکتر، تا بتوان الگوریتم‌های مکاشفه‌ای بهتر و دقیق‌تری ارائه داد.

الگوریتم‌های دقیق

سرراست‌ترین راه حل امتحان کردن تمامی جایگشتهای ممکن برای پیدا کردن ارزان‌ترین مسیر است که چون تعداد جایگشت‌ها !n است، این راه حل غیرعملی می‌شود. با استفاده از برنامه‌نویسی پویا مسئله می‌تواند با مرتبه زمانی{\displaystyle n^{2}2^{n}}{\displaystyle n^{2}2^{n}} حل شود. راه‌های دیگر استفاده از الگوریتم‌های انشعاب و تحدید برای ۴۰ تا ۶۰ شهر، استفاده از برنامه‌نویسی خطی برای کوچکتر از ۲۰۰ شهر و استفاده از روش برش-صفحه برای اندازه‌های بزرگ است.

الگوریتم‌های مکاشفه‌ای

الگوریتم‌های تقریبی متنوعی وجود دارند که خیلی سریع جواب‌های درست را با احتمال بالا به‌دست می‌دهند که می‌توان آن‌ها را به صورت زیر دسته‌بندی کرد:

  • مکاشفه‌ای سازنده
  • بهبود تکراری
    • مبادله دوبه‌دو
    • مکاشفه‌ای k-opt
    • مکاشفه‌ای V-opt
  • بهبود تصادفی

پیچیدگی محاسباتی الگوریتم فروشنده دوره گرد

این الگوریتم بطور مستقیم در مرتبه زمانی(!O(n حل می‌شود اما اگر به روش برنامه‌نویسی پویا برای حل آن استفاده کنیم مرتبه زمانی آن (O(n^2*2^n خواهد شد که جز مرتبه‌های نمایی است. باید توجه داشت علی‌رغم آنکه مرتبه نمایی مذکور زمان بسیار بدی است اما همچنان بسیار بهتر از مرتبه فاکتوریل می‌باشد. شبه کد الگوریتم فوق به صورت زیر است که در آن تعداد زیر مجموعه‌های یک مجموعه n عضوی ۲ به توان n می‌باشد و for اول یک ضریب n را نیز حاصل می‌شود که به ازای تمام شهرهای غیر مبدأ می‌باشد و حاصل (n*(2^n را پدیدمی‌آورد؛ بنابراین برای جستجوی کمترین مقدار نیاز به یک عملیات خطی از مرتبه n داریم که در زمان فوق نیز ضرب می‌شود و در نهایت زمان (n^2)*(2^n) را برای این الگوریتم حاصل می‌کند.

 

C({1},1) = 0
  for (S=2 to n)
  for All Subsets S subset of {1,2,3,...} of size S and containing1
  C(S,1) = &amp;amp;amp;
  for All J member of S , J&amp;amp;lt;&amp;amp;gt;1
  C (S , J) = min { C (S - { J } , i) + D i,J: i member of S , i &amp;amp;lt;&amp;amp;gt; J }
 return min j C ({1 . . . n}, J) + D J,1

 

شبه کد مسئله فروشنده دوره گرد

مسئله:یک تور بهینه برای یک گراف وزن دار و جهت دار مشخص نمایید. وزن‌ها اعدادی غیر منفی هستند

ورودی:یک گراف وزن دار و جهت دار و n تعداد گره‌های گراف. گراف با یک ارائه دو بعدی w مشخص می‌شود که سطرها و ستون‌هایش از ۱ تا n شاخص دهی شده‌اند و در ان [w[i][j معرف وزن لبه از گره iام به گره jام است.۴

خروجی:یک متغیر minlength که مقدار ان طول تور بهینه است و یک ارائه دو بعدی p که یک تور بهینه را از روی ان می‌توان ساخت . سطرهای p از ۱ تا n و ستونهای ان با تمامی زیر مجموعه‌های {v-{v1 شاخص دهی شده‌اند . [P[i][A شاخص اولین گره بعد از vi بر روی کوتاهترین مسیر از viتاvj است که از تمام گره‌های A دقیقاً یکبار می‌گذرد.

 

* Void travel ( int n ,
 *              const number W[][],
 * index p[][],
 * number&amp;amp;amp;minlength
* )
* {
* Index i, j, k;
* number D[1..n][subset of V-{vi}];
* for (i= 2 ; i&amp;amp;lt;=n;i++)
* D[i][∅} = w[i][1];
* for(k=1; k&amp;amp;lt;=n-2 ; k++)
* for (all subsets A v-{v1} containing k vertices
* for (i such that j≠۱ and vi is not in A){
* D[i][A] = minimum (W[i][j]+ D[vj][A-{vj}]);
* P[i][A]= value of j that gave the minimum
* }
* D[1][v-{vi}]= minimum (W[1][j]+ D[vj][V-{v1}];
* P[1][V-{v1}]= value of j that gave the minimum ;
* Minlength = D[1][V-{v1}];
* }

 

الگوریتم جستجوی ممنوعه یا Tabu Search یا به اختصار TS، یکی از قوی‌ترین الگوریتم‌ها در زمینه حل مسائل بهینه‌سازی، به خصوص مسائل بهینه‌سازی مبتنی بر گراف و مسائل بهینه‌سازی ترکیباتی (Combinatorial Optimization) است. این الگوریتم در اواخر دهه ۱۹۸۰ و توسط گلووِر (Glover) و همکارانش ارائه گردید. غالباً یکی از مسائلی که برای حل آن‌ها از الگوریتم TS استفاده می‌شود، مسئله فروشنده دوره گرد یا TSP است. این الگوریتم پاسخ‌های بسیار مناسبی را برای انواع مسائل گسسته به خصوص مسئله TSP ارائه می‌کند!

منبع


 

در مسئله فروشنده دوره گرد در پی یافتن کوتاه ترین مسیر در بین مجموعه ای از شهر ها می باشیم، به گونه ای که هر شهر فقط یک بار در مسیر قرار گرفته و مسیر ساخته شده به شهر اولی منتهی شود.

این مسئله علاوه بر جنبه نظری از جنبه عملی نیز کاربرد فراوانی دارد به عنوان مثال در مواردی مانند مسیریابی، ساخت تراشه های الکترونیکی، زمان بندی کارها و غیره مورد استفاده قرار گیرد. اما  در مواجهه با چالش حل مسائل بهینه سازی، که این نوع مسائل در دنیای واقعی بسیار زیاد هستند، روش های کلاسیک اغلب با مشکل مواجه می شوند. به همین دلیل معمولا از روشهای فرا ابتکاری همانند الگوریتم ژنتیک و سایر الگوریتم های تکاملی برای حل این نوع مسائل استفاده میشود

 

به صورت کلی مسئله فروشنده دوره گرد دارای ۳ حالت زیر می باشد.

۱-    فروشنده دوره گرد متقارن

در حالت متقارن مسئله، تعدادی شهر داریم و هزینه رفتن مستقیم از یکی به دیگری را می‌دانیم .مطلوب است کم ‌هزینه‌ترین مسیری که از یک شهر شروع شود و از تمامی شهرها دقیقا یکبار عبور کند و به شهر شروع بازگردد.

۲-   فروشنده دوره گرد نامتقارن

مسأله ­ي فروشنده ­ي دوره­ گرد نامتقارن, یک TSP است که فاصله بين رئوس آن, متقارن نيست. ATSP بسيار مشکل­تر از TSP است، در حقيقت در حالي که TSP متقارن, حتي در گراف­هاي با چندين هزار  رأس, به طور بهينه, قابل حل است, تنها نمونه­هاي خاصي ازATSP را که ماتريس فاصله­ي آنها, تقريباً متقارن است, تنها در گراف­هاي داراي چندين دوجين رأس, مي­توان به طور بهينه حل کرد. به کاربردن هوش مصنوعی  براي ATSP, راحت­ و سر راست است. چون هيچ تغييراتي در الگوريتم اصلي, لازم ندارد. پيچيدگي محاسباتي در حلقه­ي الگوريتم, برنامه­ي کاربردي TSP, يکسان است, زيرا تنها تفاوت آنها در فاصله­ها و ماتريس­هاي ردپا است که در اينجا ديگر متقارن نيستند.

۳-   فروشنده دوره گرد با پنجره های زمانی

مسئله فروشنده دوره گرد با پنجره زمانی، شامل یافتن کوتاهترین طول توری است که به وسیله یک فروشنده دوره گرد طی می شود با این شرایط که فروشنده باید هر گره را فقط یکبار ملاقات کند و در پنجره زمانی معینی به آن سرویس دهد. به این معنا که اگر فروشنده زودتر از محدوده زمانی تعیین شده به آن گره برسد باید منتظر بماند تا بازه زمانی سرویس دهی مربوط به آن گره شروع شود. همچنین اگر دیرتر از پنجره زمانی برسد ارائه سرویس به آن گره دیگر امکان پذیر نخواهد بود.

 

دوربین های عکاسی آنالوگ

دوربین آنالوگ دستگاهی برای ثبت عکس بر روی فیلم عکاسی (سطح حساس به نور) می‌باشد و تصویر گرفته شده بر روی فیلم بعد از ظهور بصورت نگاتیو یا منفی (ریورسال) قابل رویت است. دوربین عکاسی آنالوگ بصورت‌های:

  1. کاملاً مکانیکی،
  2. نیمه خودکار،
  3. کاملاً خودکار (ناوبری الکترونیکی)،

طراحی و ساخته شده است.

اولین دوربین ۳۵ میلیمتری با قابلیت تعویض نمایاب و لنز(system camera)، نیکون اِف

اولین دوربین ۳۵ میلیمتری با قابلیت تعویض نمایاب و لنز(system camera)، نیکون اِف

تاریخچه

اتاق تاریک، اولین قدم بزرگ در راه پیدایش عکاسی بود که توسط نقاشان ایتالیا یی در طی قرن شانزدهم میلادی برداشته شد. برای بدست آوردن حداکثر وضوح، تنظیم فاصله روزنه از دیواری که تصویر روی آن بازتابیده می‌شد، از مشکلات اصلی در این سیستم بشمار می‌رفت. رفع این مشکل، باعث ورود عدسی به دنیای عکس و تصویر گردید.

انواع دوربین های آنالوگ

  1. دوربین سوراخ سوزنی (Pin Hole)،
  2. دوربین تک‌لنزی غیربازتابی (rangefinder camera)،
  3. دوربین تک‌لنزی بازتابی (Single Lens Reflex)،
  4. دوربین دولنزی بازتابی (Twin-lens reflex)،
  5. دوربین قطع بزرگ (View Camera).

سیستم ضبط تصویر

در یک دوربین آنالوگ، فیلم حساس به نور، تصویر را ذخیره می‌سازد و بعد از عملیات شیمیایی برای نگهداری تصویر از آن استفاده می‌شود.

نگارخانه

دوربین سوراخ سوزنی کداک رتینا ۱۹۵۷ آساهی فلکس ۱۹۵۵ رولی فلکس دوربین دولنزی بازتابی (Voigtländer Brillant). اولین دوربین تک لنزی بازتابی، کانتکس اس، تولید سال ۱۹۴۹ نیکون اِف ۱۹۵۹، اولین دوربین ۳۵ میلیمتری با قابلیت تعویض نمایاب و لنز دوربین ۵*۷ اینچ تویو.

دوربین آنالوگ به دوربینی گفته میشود که با دست و یا دستگاه های مکانیکی خود دوربین تنظیم میشود و دارای فیلم است. ساختار دوربین های آنالوگ بر این اساس است که؛ نور از داخل لنز گذشته و پس از برخورد با یک آینه، به سوی چشم ناظر هدایت می شود. وقتی عکاس کادر مناسب و دیگر پارامترها را تنظیم کرد، دکمه شاتر را می فشارد و با این کار بخش هایی که مانع رسیدن نور به صفحه حساس می شده اند، از میان برداشته می شوند. طبیعتاً با بالا رفتن آینه، عکاس قادر به مشاهده آن چه دوربین به سوی آن نشانه رفته است، نیست. با توجه به فاصله کانونی لنز، تصویر مطلوب در نقطه ای خاص و وارونه تصویر اصلی تشکیل می شود. دوربین و فوکوسر آن نیز سبب می شوند این تصویر درست روی محل مورد نظر عکاس؛ یعنی، صفحه حساس(که در دوریبن های آنالوگ، فیلم است) تشکیل شود.

اجزای مختلف یک دوربین آنالوگ

۱) لنز دوربین
۲)نگه دارنده ی لنز
۳)دیافراگم
۴)چرخاننده ی فیلم
۵)فیلم عکاسی
۶)محل اتصال بند دوربین
۷)شاتر
۸)دکمه ی کنترل سرعت عکسبرداری
۹)صفحه ی مشخصات عکس و شارژ دوربین(در دوربین هایی که باتری دارند)
۱۰)ویزور
۱۱)محل اتصال فلش خارجی
۱۲)حلقه ی فوکوس

مراحل گرفته شدن یک عکس

هنگامی که فیلم داخل دوربین قرار میگیرد معمولا ۲-۳ تا فریم به علت در معرض نور قرار گرفتن میسوزند بنابراین بهتر است فیلم ها رو ۲-۳ تا جلو بزنیم. جلو زدن فیلم به این صورت است که یک دسته ی مکانیکی که در بالای دوربین قرار دارد را با چرخاندن آن تا انتها، میله ی چرخاننده ی فیلم می چرخد و فیلم یک فریم کامل جلو میرود! معمولا برای اولین استفاده از فیلم باید ۲-۳ بار این حرکت رو تکرار کرد.
دقت کنید وقتی یک فریم رو کامل جلو میبرید تا اینکه یک بار شاتر فشرده نشود (در واقع عکسی گرفته نشود) نمیتوان دسته ی چرخاننده ی فیلم را حرکت داد. از روش معکوس این حرکت (چرخاندن فیلم در خلاف جهت) هم میتوان برای تکنیک هایی مانند مولتی اکسپوز استفاده کرد.و هم با فشردن دکمه ی آزاد کننده ی فیلم، برای جمع کردن کامل فیلم برای ظهور عکس. (جزئیات در برخی دوربین ها کمی متفاوت است) بعد از اینکه یک فریم آماده ی عکس گرفتن شد و پس از انتخاب کادر مناسب و تنظیم دیافراگم مناسب با توجه به حساسیت فیلم مورد استفاده قرار گرفته و فوکوس صحیح دکمه ی شاتر را فشار میدهیم.

آینه بالا می رود و پرده ی شاتر هم بالا میرود. (در دوربین های آنالوگ برخی پرده ها یک تکه هستند و برخی ۳ تکه که پرده های ۳ تکه طبیعتا خیلی بهتر هستند) نور وارد شده به لنز در برخورد به نگاتیو به مواد شیمیایی ای که روی فیلم پوشونده شده برخورد میکند این نور در واقع انرژی فعال سازی یک واکنش شیمیایی است. این مواد شیمیایی با توجه به شدت و رنگ نور (طول موج) واکنش شیمیایی انجام می دهند که بعدا در ظهور عکس، ظاهر کننده این مواد شیمیایی که واکنش های مختلف دادند رو از هم تفکیک کرده و هر فراورده روی صفحه ی فیلم رو به صورت رنگ های نگاتیو (منفی نور های عکس) در می آورد.

یکی از مزیت های دوربین آنالوگ نشان دادن خیلی خوب و واقعی رنگ است. اما اگر به یک نقطه نور زیاد برسد (در واقع اور اکسپوز بشود) آن قسمت کاملا سفید خواهد شد.سپس فیلم ظاهر شده اسکن و اینورت می شود و عکس قابلیت چاپ پیدا می کند. حساسیت فیلم های مورد استفاده قرار گرفته به مواد شیمیایی موجود در اون وابسته است.
فیلم های سیاه سفید از حساسیت ۱۰۰ تا ۳۲۰۰ یا حتی ۶۴۰۰ برخوردارند اما فیلم های رنگی معمولا دارای حساسیت های ۱۰۰ و ۲۰۰ و ۴۰۰ هستند. هرچه حساسیت بیشتر باشد ذرات شیمیایی روی صفحه فیلم بزرگتر هستندکه این باعث پایین آمدن کیفیت عکس خواهد شد ولی فقط در چاپ به ابعاد خیلی بزرگ این افت کیفیت احساس میشود.

مزایای عکاسی آنالوگ

۱- مصرف باطری به مراتب کمتر نسبت به عکاسی دیجیتال و عدم احتمال ایجاد مشکل در کار عکاسی های طولانی مدت به علت تمام شدن باطری
۲- عدم وجود نویز در نوردهی های طولانی مدت
۳- عدم وابستگی کیفیت عکس به نوع دوربین.البته به لنز بستگی دارد ولی به بدنه دوربین خیلی وابسته نیست و کیفیت عکس به نوع فیلم مورد استفاده بستگی دارد.
۴- بیشتر بودن دامنه دینامیکی فیلم نسبت به سنسور دیجیتال
۵- ارزان تر بودن دوربین های فیلمی
۶- امکان استفاده از فیلم های مختلف در یک دوربین ( مثل حساس به مادن قرمز و … )
۷- دوربین های فیلمی نسبت به دیجیتال ها در برابر آسیب ها حساسیت کمتری دارند.ضربه٬ گرد و غبار٬ رطوبت و …
۸- امکان خرابی کمتری دارند و در شرایط بحرانی قابل اطمینان تر هستند.


مزایای دوربین های عکاسی دیجیتالی:

۱- مرور فوری عکس ، بدون این که عکاس منتظر شود که عکس ظاهر شود. اگر مشکلی در عکس باشد ، عکاس میتواند مشکل را فورا تصحیح کند و عکس دیگری بگیرد.

۲- فقط عکس های خوب چاپ میشود، در نتیجه کاربر میتواند تعداد زیادی عکس با اختلافات جزئی و تنظیمات مختلف از یک صحنه بگیرد و بعد بهترین ان را انتخاب و چاپ کند.

۳- اگر شخص رایانه داشته باشد ، ذخیره دائم عکسها ارزانتر از فیلم از کار در می اید.

۴- عکس ها را میتوان از یک جایی به جای دیگر کپی کرد بدون اینکه کیفیت ان کاهش یابد.

۵- هر کس میتواند با داشتن رایانه و یک پرینتر معمولی ، عکس های خودش را چاپ کند. تازه با استفاده از پرینت های مخصوص دوربین ها ، به رایانه هم نیازی نیست و دوربین را میتوان مستقیما به پرینتر وصل کرد.

۶- دوربین های دیجیتال میتوانند کوچکتر از دوربین های انالوگ با همان کیفیت عکس ، ساخته شوند.

۷- قابلیت استفاده کردن داده هایی مثل زمان و تاریخ عکاسی، مدل دوربین، سرعت شاتر، سرعت فیلم و دیگر موارد به فایل عکس مورد نظر، در حالی که این قابلیت در فیلم های عکاسی فقط به چاپ تاریخ روی عکس ها محدود میشود.

۸- در دوربین های دیجیتال از خیلی افکت های تصویری میتوان استفاده کرد که در دوربین های فیلمی امکان ندارد.

۹- قابلیت گرفتن صدها عکس بدون این که نیازی به تغییر چیزی باشد. در حالی که در دوربین های فیلمی بعد از ۲۴ یا ۳۶ عکس باید فیلم را عوض کرد.

۱۰- خیلی از دوربین های دیجیتال،خروجی AV دارند که نشان دادن عکس ها را به دیگران در تلویزیون ممکن میسازد.

۱۱- عکاسی دیجیتال این امکان را فراهم میکند که شما تنظیمات مختلف دوربین و سبکهای مختلف عکاسی را تجزیه کنید و تکنیک عکاسی تان را بهبود بخشید ، بدون این که لازم باشد هزینه زیادی بدهید از نظر زمان و از نظر وقت و انرژی.

۱۲- ابزار ضد تکان دوربین های دیجیتال ، گرفتن عکس های ترو تمیز با دوربین را روی دست ممکن میکند در حالی که قبلا حتما نیاز به سه پایه بود.

۱۳- دیگر هر کس یک تاریک خانه ، خانگی دارد و میتواند با رایانه و نرم افزار تغییرات لازم را در عکس ها بدهد.

۱۴- مقادیر ISO را به راحتی میتوان در وسط عکس تغییر داد. مثلا وقتی هوا افتابی است ولی یکدفعه ابری میشود ، قبلا لازم بود که فیلم را دربیاورید و فیلم جدید با مقدار ISO مناسب را داخل دوربین بگذارید ولی با دوربین دیجیتال این تغییرات فقط با فشار چند دگمه انجام میشود.

۱۵- برای فرستادن عکسها به داخل رایانه ، دیگر نیازی به اسکنر نیست.

 ویژگی های منفی دوربین دیجیتال نسبت به دوربین آنالوگ :

۱- مصرف انرژی باطری های دوربین های دیجیتال نسبت به دوربین های فیلم دار خیلی بیشتر است در نتیجه ممکن است وسط عکاسی یک دفعه با دوربین خاموش مواجه شویم.

۲- استناد عکس های دیجیتال نسبت به عکس های فیلمی کمتر است چون میشود در انها دستکاری کرد البته بعضی از تولید کنندگان تلاش میکنند به روشهایی برای تشخیص عکس های دستکاری شده برسند تا این ضعف را جبران کنند.

۳- سنسورهای دیجیتال اغلب دامنه دینامیکی کمتری نسبت به فیلم چاپی رنگی دارند البته تعدادی از سنسور های CCD جدیدتر مثل FUJIS SUPER CCD که دیودها با حساسیت های مختلف را با هم ترکیب کرده اند برای حل این مشکل به میدان امده اند.

۴- در بعضی از عکس های دیجیتالی نویز تصویری چند رنگ قابل مشاهده است.

۵- ویرایش و پردازش فایل های RAW ( فایلهای حرفه ای عکاسی ) خیلی طول می کشد.

۶- برای عکاسی در محل های پرت و دور افتاده عکاس باید کلی باطری با خودش حمل کند که وزن بار عکاس را افزایش میدهد و کار را برای او سخت تر میکند.

تفاوت تصویر دوربین آنالوگ با دوربین دیجیتال

منبع : http://www.kingit.ir

دلایل استفاده از دوربین آنالوگ توسط کاربران امروزی

۱ . فیلم همچنان خوب است

همه ما کمابیش شانس آن را داشته‌ایم که دوره عکاسی فیلم را تجربه کنیم. عکس‌های خانوادگی ما شامل سفرها و گردش ها و جشن‌های خانوادگی همگی با دوربین‌های آنالوگ و فیلم عکاسی شده‌اند. اما همچنان که آلبوم عکس‌های آ سال‌ها را نگاه می‌کنیم، متوجه کیفیت بارز و خوب عکس‌ها می‌شویم. همچنان عکس‌ها آنقدر کیفیت دارند که با عکس‌های بهترین دوربین‌های دیجیتال امروز برابری کنند.

فیلم همچنان خوب است

امروزه با وجود اینکه ما عکس‌های زیادی چه با دوربین و چه با اسمارتفون خود ثبت می‌کنیم، اما تقریباً آلبومی برای نگهداری آنها نداریم. به همین دلیل یکی از دغدغه‌های ما نگهداری و ساماندهی عکس‌ها است. با این حال هیچ وقت این اتفاق نمی‌افتد. اما در گذشته اینطور نبود و با گرفتن عکس، پس از چاپ عکس‌ها به صورت منظم به آلبوم عکس منتقل می‌شدند. همچنان که ما امروز از دوره فیلم عکس بیش‌تری می‌بینیم.

۲ . «داینامیک رنج» فیلم بیش‌تر است

اشتباه نکنید، تکنیک عکس «HDR» مدتها است که در عکاسی وجود دارد، اما در عکاسی دیجیتال مدت کوتاهی است که باب شده‌است. در عکاسی دیجیتال به دلیل محدودیت‌های موجود، برای رسیدن به یک عکس HDR معقول باید از یک صحنه ۳ عکس با نوردهی‌های متفاوت ثبت کرد. اما عکسی که با فیلم ثبت شده‌باشد، آنقدر داینامیک رنج بالایی دارد که به تنهایی می‌توان آن را به عکس HDR تبدیل کرد.

«داینامیک رنج» فیلم بیش‌تر است

یک فیلم سیاه و سفید بیش‌تر از ۶ پله در تاریکی و ۶ پله در روشنایی داینامیک رنج دارد. همچنین یک فیلم رنگی به راحتی در دو پله از هر سمت روشنایی و تاریکی قابلیت بازگشت با جزئیات دارد. علاوه بر این‌ها یک عکس فیلم بسیار به آنچه که ما با چشم خود می‌بینیم نزدیک‌تر است.

۳ . فیلم باعث آرامش می‌شود

وقتی با عکاسی دیجیتال طرف هستیم، از یک سوژه ده‌ها عکس با ترکیب‌بندی‌های مختلف ثبت می‌کنیم. با این وجود باز هم دقت کافی در ثبت عکس نکرده و بسیاری از مشکلات عکس را به فتوشاپ می‌سپاریم.

فیلم باعث آرامش می‌شود

اما در عکاسی فیلم اینگونه نیست. باید با حوصله ترکیب‌بندی کرده و بهترین نورسنجی را انجام دهیم. با دقت فراوان و آرامش از یک سوژه تنها یک عکس ثبت می‌کنیم. این دقت و حصله باعث ایجاد آرامش در عکاس می‌شود.

۴ . فیلم امنیت بیش‌تری دارد

قدیمی‌ترین عکس دیجیتالی که دارید متعلق به چه زمانی‌است؟ آن را در کجا ذخیره کرده‌اید؟ با وجود پیشرفت بسیار زیاد فناوری، اما همچنان باید گفت که عکس‌های دیجیتال امنیت لازم را ندارند. آنها تنها چند فایل کامپیوتری هستند که با یک اتفاق ساده امکان از بین رفتن و یا آسیب دیدن آنها وجود دارد. حتی نظریه‌ای وجود دارد که ممکن است قرن حاضر به قرن فراموش شده تبدیل شود.

فیلم امنیت بیش‌تری دارد

در مورد فیلم قضیه کاملاً متفاوت است. به صورت اولیه عکس‌ها چاپ می‌شوند و همیشه نسخه‌ واقعی از آنها وجود دارد. علاوه بر این نگاتیو عکس‌ها نیز آرشیو می‌شوند و می‌توانند دوباره چاپ شوند. همانطور که می‌دانید درحال حاضر هر نگاتیوی که از گذشته پیدا شود به راحتی قابل چاپ و حتی ترمیم است. مثال آن عکس‌هایی است که از بیش از ۱۰۰ پیش کشف می‌شوند و به راحتی و با کیفیت بالا چاپ می‌شوند.

۵ . فرآیند ظهور و چاپ در فیلم بسیار لذت بخش است

یکی از جواب‌هایی که برخی از عکاسان به ما دادند، دلبستگی آنها به فرآیند ظهور فیلم و چاپ عکس بود. در عکاسی دیجیتال به محض گرفتن عکس می‌توانید نتیجه کار را ببینید. اما در فیلم این پروسه کاملاً متفاوت است.

فرآیند ظهور و چاپ در فیلم بسیار لذت بخش است

پس از ثبت عکس باید آن را ظهور کرد. مراحل ظهور فیلم و استفاده از داروهای مختلف برای بسیاری از عکاسان بسیار دلپذیر و آرام‌بخش است. پس از طری مراحل ظهور با مرحله چاپ طرف هستیم که در آنجا به مرور عکس در مقابل عکاس شکل می‌گیرد و این دلچسب‌ترین لحظه کار است. لحظه‌ای که نتیجه کار دیده می‌شود.

۶ . نیازی به برق ندارید

تصور کنید دنیا به پایان خود رسیده‌است و شرایط طوری است که برقی برای استفاده وجود ندارد؛ چگونه باطری دوربین خود را شارژ خواهید کرد؟ خارج از شوخی، باید اذعان کرد که بشر امروز به شکل کامل وابسته به انرژی برق است و اگر در شرایطی قرار بگیرد که برق در دسترس نباشد، عملاً از زندگی ساقط می‌شود. در مورد عکاسی دیجیتال هم این قضیه صدق می‌کند و به محض نبود برق، عملاً عکاسی هم تعطیل است.

نیازی به برق ندارید

فرآیند عکاسی با فیلم از ابتدا تا انتها هیچ وابستگی‌ای به برق ندارد. تمام اتفاقاتی که بر روی فیلم می‌افتد یک فرآیند شیمیایی است که در واکنش به نور اتفاق می‌افتد. از این‌رو در هر شرایطی امکان عکاسی وجود دارد.

۷ . فیلم «چشم ‌نواز» تر است

حتی اگر طرفدار این نظریه هم نباشید، حتماً قبول دارید که فیلم رنگ و حسی متفاوت از عکس دیجیتال دارد. درحال حاضر فیلترهای مختلفی بر روی نرم‌افزارهای مختلف برای اعمال بر روی عکس‌های دیجیتال وجود دارد تا آنها را به فیلم شبیه کند. اما واقعیت این است که با وجود نتایج خوب، همچنان فیلم حسی متفاوت دارد که با دیجیتال قابل دستیابی نیست.

فیلم «چشم ‌نواز» تر است

دلیل همه این‌ها فرآیند کاملاً متفاوت فیلم و دیجیتال در ثبت یک عکس است. بسیاری اعتقاد دارند که این چشم‌نواز تر بودن فیلم، به دلیل شبیه‌ت بودن آن به آنچیزی است که با چشم خود می‌بینیم.

۸ . عکس دیجیتال واقعیت جعلی است

همانطور که می‌دانید یک عکس دیجیتال از مجموعه‌ای از پیکسل‌ها تشکیل می‌شود. این پیکسل‌ها به خودی خود هویتی ندارند و تنها نمایش دهنده یک رنگ هستند. از این رو بسیاری بر این باورند که عکس دیجیتال تنها یک واقعیت جعلی هستند و به همین دلیل آنها را رد می‌کنند.

عکس دیجیتال واقعیت جعلی است

در فیلم قضیه متفاوت است. هر فیلم تشکیل شده از بلورهای ریزی است که در اندازه‌های متفاوت و به شکلی نامنظم بر روی سطح فیلم قرار گرفته‌اند. حتی اگر با یک میکروسکوپ هم سطح یک فیلم را نگاه کنید، باز هم به یک شمای کلی از تصویر می‌رسید، اما در عهکس دیجیتال تنها با چند موزاییک طرف خواهید شد.

۹ . دوربین‌های فیلم ارزان‌تر هستند

در حال حاضر اگر بخواهید جدید‌ترین دوربین‌های دیجیتال DSLR را بخرید باید بیش از ۳ هزار دلار هزینه کنید و این مبلغ تنها برای بدنه آنها است. هزینه دیگری هم باید برای خرید لنز در نظر بگیرید. حتی دوربین‌های حد متوسط و یا دست دوم‌ها هم گران هستند.

دوربین‌های فیلم ارزان‌تر هستند

دوربین‌های فیلم اما در بهترین کیفیتشان با کمترین قیمت قابل خرید هستند. هزینه‌ای که برای خرید بهترین دوربین فیلم خواهید کرد کمتر از یک دهم خرید بهترین دوربین DSLR است.

۱۰ . برای متفاوت بودن

در حال حاضر روزانه میلیون‌ها عکس در شبکه‌های اجتماعی و وبسایت‌ها منتشر می‌شود. گرفتن عکسی که از میان این همه عکس قابل توجه باشد نیازمند منحصر بفرد بودن و تفاوت است. یکی از راه‌های ایجاد تفاوت در عکس، ثبت آنها با فیلم است.

برای متفاوت بودن

علاوه بر تفاوتی که بین دیجیتال و فیلم وجود دارد، گرفتن عکس با دوربین‌های فیلم خاص نیز می‌تواند خلاقیت‌های ویژه‌ای برای شما به وجود بیاورد. دوربین‌هایی که هیچ کس کار با آنها را حتی بلد هم نیست.

۱۱ . به خاطر ایرادهایش

در حال حاضر جنبشی در میان برخی از عکاسان به وجود آمده است که با استفاده از ایرادهای دوربین‌های فیلم ارزان قیمت، اثر هنری خلق می‌کنند. به عنوان مثال محصول مورد علاقه آها دوربین‌های فیلم یکبار مصرف هستند که به دلیل مشکلات عدیده‌شان، نتیجه متفاوت و گاهی بسیار جذاب ارائه می‌کنند.

به خاطر ایرادهایش

۱۲ . به دلیل مرموز بودن فیلم

وقتی عکس دیجیتال می‌گیرید، به محض ثبت عکس آن را در نمایشگر دوربین می‌بینید. اما یکی از جذابیت‌های عکاسی فیلم، در نداشتن همین قابلیت است.وقتی با فیلم عکس ثبت می‌کنید باید صبر کنید تا فیلم ظهور شود و چاپ شود تا نتیجه کار مشخص شود.

به دلیل مرموز بودن فیلم

تصور کنید در مسافرت هستید. باید صبر کنید تا از مسافرت برگردید و فیلم خود را به عکاسی بدهید و بعد از آن هم مدتی صبر کنید تا عکاسی عکس‌های چاپ شده شما را تحویل دهد. این فاصله و نهایتاً دیدن نتیجه کار بسیار جذاب و دلچسب است که گاهی شما را شگفت زده می‌کند.

 


منابع

۱٫http://fa.wikipedia.org

۲٫ http://forum.avastarco.com

۳٫ http://www.kingit.ir

۴٫ http://farnet.ir

با سلام. قصد دارم در این پست تعدادی از منابع اصلی آموش پردازش تصویر و بینایی ماشین رو معرفی کنم.

امیدوارم که مفید باشد

 

لگچرهای کتاب آقای گنزالس

تعداد فایل : ۱۷ عدد

فرمت: pdf

زبان : انگلیسی

نویسنده: گنزالس

پسورد فایل: behsanandish.com

دانلود

 

______________________________________

 

مفاهیم پایه پردازش تصویر دانشگاه شهید بهشتی

تعداد صفحه: ۱۰۹ صفحه

فرمت: pdf

زبان : فارسی

نویسنده: ‫احمد محمودی ازناوه‬

پسورد فایل: behsanandish.com

دانلود

فهرست مطالب:
‫• مقدمه اي بر پردازش تصوير‬
‫– كاربردهاي پردازش تصوير‬
‫• ساختار تصوير ديجيتال‬
‫• تصاوير رنگي‬
‫• حساسيت چشم‬
‫– تباين‬
‫• حسگرهاي تصوير‬
‫• آشنايي با ‪Matlab‬‬
‫• آشنايي با فضارنگها‬

 

______________________________________

 

 

روش بهینه‌سازی ازدحام ذرات

روش بهینه‌سازی ازدحام ذرات (به انگلیسی: Particle swarm optimization) یا به اختصار روش PSO، یک روش سراسری کمینه‌سازی است که با استفاده از آن می‌توان با مسائلی که جواب آن‌ها یک نقطه یا سطح در فضای n بعدی می‌باشد، برخورد نمود. در اینچنین فضایی، فرضیاتی مطرح می‌شود و یک سرعت ابتدایی به آن‌ها اختصاص داده می‌شود، همچنین کانال‌های ارتباطی بین ذرات درنظر گرفته می‌شود. سپس این ذرات در فضای پاسخ حرکت می‌کنند، و نتایج حاصله بر مبنای یک «ملاک شایستگی» پس از هر بازهٔ زمانی محاسبه می‌شود. با گذشت زمان، ذرات به سمت ذراتی که دارای ملاک شایستگی بالاتری هستند و در گروه ارتباطی یکسانی قرار دارند، شتاب می‌گیرند. علی‌رغم اینکه هر روش در محدوده‌ای از مسائل به خوبی کار می‌کند، این روش در حل مسائل بهینه‌سازی پیوسته موفقیت بسیاری از خود نشان داده است.

انواع الگوریتم ازدحام ذرات

الگوریتم ازدحام ذرات پیوسته

مقدمه

الگوریتم PSO یک الگوریتم جستجوی جمعی است که از روی رفتار اجتماعی دسته‌های پرندگان مدل شده‌است. در ابتدا این الگوریتم به منظور کشف الگوهای حاکم بر پرواز هم‌زمان پرندگان و تغییر ناگهانی مسیر آن‌ها و تغییر شکل بهینهٔ دسته به کار گرفته شد. در PSO، ذرات در فضای جستجو جاری می‌شوند. تغییر مکان ذرات در فضای جستجو تحت تأثیر تجربه و دانش خودشان و همسایگانشان است؛ بنابراین موقعیت دیگر توده ذرات روی چگونگی جستجوی یک ذره اثر می‌گذارد. نتیجهٔ مدل‌سازی این رفتار اجتماعی فرایند جستجویی است که ذرات به سمت نواحی موفق میل می‌کنند. ذرات از یکدیگر می‌آموزند و بر مبنای دانش بدست آمده به سمت بهترین همسایگان خود می‌روند اساس کار PSO بر این اصل استوار است که در هر لحظه هر ذره مکان خود را در فضای جستجو با توجه به بهترین مکانی که تاکنون در آن قرار گرفته‌است و بهترین مکانی که در کل همسایگی‌اش وجود دارد، تنظیم می‌کند.

در ادامه کمی به توضیح مفهوم هوش جمعی میپردازیم.

هوش جمعی

هوش جمعی خاصیتی است سیستماتیک که در این سیستم، عاملها به‌طور محلی با هم همکاری می‌نمایند و رفتار جمعی تمام عاملها باعث یک همگرایی در نقطهای نزدیک به جواب بهینه سراسری می‌شود نقطه قوت این الگوریتمها عدم نیاز آنها به یک کنترل سراسری می‌باشد. هر ذره) عامل) در این الگوریتم‌ها خود مختاری نسبی دارد که می‌تواند در سراسر فضای جواب‌ها حرکت کند و می‌بایست با سایر ذرات (عامل‌ها) همکاری داشته باشد. دو الگوریتم مشهور هوش جمعی، بهینه‌سازی لانه مورچگان و بهینه‌سازی توده ذرات می‌باشند. از هر دو این الگوریتم‌ها می‌توان برای تعلیم شبکه‌های عصبی بهره برد.

اولین الگوریتم ازدحام ذرات

در سال ۱۹۹۵ ابرهارت و کندی برای اولین بار PSO به عنوان یک روش جستجوی غیر قطعی برای بهینه‌سازی تابعی مطرح گشت این الگوریتم از حرکت دسته جمعی پرندگانی که به دنبال غذا میباشند، الهام گرفته شده‌است. گروهی از پرندگان در فضایی به صورت تصادفی دنبال غذا میگردند. تنها یک تکه غذا در فضای مورد جستجو وجود دارد. هر راه حل که به آن یک ذره گفته میشود، PSO در الگوریتم معادل یک پرنده در الگوریتم حرکت جمعی پرندگان میباشد. هر ذره یک مقدار شایستگی دارد که توسط یک تابع شایستگی محاسبه میشود. هر چه ذره در فضای جستجو به هدف (غذا در مدل حرکت پرندگان) نزدیکتر باشد، شایستگی بیشتری دارد. همچنین هر ذره دارای یک سرعت است که هدایت حرکت ذره را بر عهده دارد. هر ذره با دنبال کردن ذرات بهینه در حالت فعلی، به حرکت خود در فضای مسئله ادامه میدهد.

به ا ین شکل است که گروهٔ از ذرات در آغاز کار به صورت تصادفی به وجود می‌آیند و با به روز کردن نسلها سعی در یافتن راه‌حل بهینه می‌نمایند. در هر گام، هر ذره با استفاده از دو بهترین مقدار به روز می‌شود. اولین مورد، بهترین موقعیتی است که تاکنون ذره موفق به رسیدن به آن شده‌است. موقعیت مذکور شناخته و نگهداری می‌شود که این بهترین مقدار نوستالژی آن ذره نیز گفته میشود که آن را با pbest نمایش میدهیم. بهترین مقدار دیگری که توسط الگوریتم مورد استفاده قرار میگیرد، بهترین موقعیتی است که تا کنون توسط جمعیت ذرات بدست آمده‌است که آن را gbest میگوییم (هوش جمعی).

پس از یافتن بهترین مقادیر، سرعت و مکان هر ذره با استفاده از رابطه ۶ و ۷به روز می‌شود.

رابطه ۶{\displaystyle v(t+1)=v(t)+c1*rand(t)*(pbest(t)-position(t)+c2*rand(t)*(gbest(t)-position(t))}

رابطه ۷ {\displaystyle position(t+1)=position(t)+v(t+1)}

سمت راست معادله ۶ از سه قسمت تشکیل شده‌است که قسمت اول، سرعت فعلی ذره است ({\displaystyle v(t)}) و قسمتهای دوم ({\displaystyle c1*rand(t)*(pbest(t)-position(t))}) و سوم ({\displaystyle c2*rand(t)*(gbest(t)-position(t))}) میزان تغییر سرعت ذره و جهت آن به سمت بهترین تجربه شخصی (نوستالژی) و بهترین تجربه گروه (هوش جمعی) را به عهده دارند. اگر قسمت اول را در این معادله درنظر نگیریم ({\displaystyle v(t)})، آنگاه سرعت ذرات تنها با توجه به موقعیت فعلی و بهترین تجربه ذره و بهترین تجربه جمع تعیین می‌شود و عملاً تأثیر سرعت کنونی و لختی آن حذف می‌شود. به این ترتیب، بهترین ذره گروه، در جای خود ثابت می‌ماند و سایرین به سمت آن ذره حرکت می‌کنند. در واقع حرکت دسته جمعی ذرات بدون قسمت اول معادله ۶، پروسه ای خواهد بود که طی آن فضای جستجو به تدریج کوچک می‌شود و جستجویی محلی حول بهترین ذره شکل می‌گیرد. در مقابل اگر فقط قسمت اول معادله ۶ را در نظر بگیریم، ذرات راه عادی خود را می‌روند تا به دیواره محدوده برسند و به نوعی جستجویی سراسری را انجام میدهند. پارامترهای c1 و c2 (مقدار آن حدود ۲ است) میزان اهمیت و وزن هوش جمعی و نوستالژی را مشخص می‌کنند. پارامتر {\displaystyle rand(t)}

با صفر قرار دادن یا ندادن پارامترهای c1 و c2 سه حالت مختلف زیر به وجود می‌آید:

  1. هیچ‌کدام از این دو پارامتر را صفر نگذاریم که میشود pso با لختی سرعت، هوش جمعی و نوستالژی.
  2. وزن نوستالژی را صفر کنیم و فقط براساس هوش جمعی و لختی عمل کنیم.
  3. وزن هوش جمعی را صفر کنیم و فقط براساس نوستالژی و لختی عمل کنیم.

برای مقداردهی اولیه توسط رابطه ۸ عمل میکنیم. سرعت اولیه نیز طبق ۹ صفر است.

رابطه ۸ {\displaystyle x(0)=x_{min}+rand(x_{max}-x_{min})}

رابطه ۹ {\displaystyle v(0)=0}

شبه کد الگوریتم PSO:

For each particle
    Initialize particle
End For
Do
For each particle
    Calculate fitness value of the particle fp
    /*updating particle’s best fitness value so far)*/
    If fp is better than pBest
    set current value as the new pBest
End For
/*updating population’s best fitness value so far)*/
Set gBest to the best fitness value of all particles
For each particle
    Calculate particle velocity according equation
    Update particle position according equation
End For While maximum iterations OR minimum error criteria is not attained

اما در مورد شرط توقف باید گفت که راههای زیر موجود است:

  • ‌ تعداد تکرار معین
  • رسیدن به یک شایستگی آستانه
  • یک تعداد تکرار که شایستگی تغییر نکند (مثلاً اگر بعد از ۱۰ تکرار شایستگی ثابت بود و بهتر نشد).
  • راه آخر بر اساس چگالی تجمیع اطراف نقطه بهینه است. به این صورت که میگوییم اگر ۸۰ درصد ذرات در فاصلهای کمتر از ۲۰ درصد بیشترین فاصله نسبت به بهترین جواب قرار داشتند، الگوریتم باید متوقف شود.

روش آخر به این صورت است که طبق رابطه ۱۰ می‌توان را بدست آورد. همان‌طور که گفته شد{\displaystyle R_{norm}} یک مقدار بین ۰ و ۱ است و همین‌طور F بیشترین فاصله بین دو ذره در حالت کنونی است.

رابطه ۱۰ {\displaystyle R_{norm}=\left({\frac {R_{max}}{F}}\right)}

مشکل اساسی اولین الگوریتم ازدحام ذرات

یک مشکل اساسی فرمول ارائه شده برای الگوریتم ازدحام ذرات اولیه اینست که دائماً سرعت افزایش می‌یابد و هیچ برنامه‌ای برای کاهش آن ارائه نشده است، درصورتیکه لازم است هر چه به بهینه سراسری نزدیک می‌شویم سرعت کمتر شود تا ذره ما از بهینه سراسری عبور نکند.

برای این کار چند راه حل ارائه شده‌است.

استفاده از سرعت بیشینه برشی

در این روش برای جلوگیری از زیاد شدن بیش از اندازه سرعت، یک سرعت بیشینه تعریف میشود تا هرگاه سرعت به بیش از آن رسید برش داده شود.

{\displaystyle v\prime (t+1)={\begin{cases}v(t+1)&v(t+1)<v_{max}\\v_{max}&v(t+1)>=v_{max}\end{cases}}}

استفاده از سرعت بیشینه تانژانت هیپربولیکی

در این روش از یک تابع تانژانت هیپربولیک برای محدود کردن سرعت به یک مقدار خاص استفاده می‌شود. تفاوت این حالت با حالت برشی اینست که در این حالت تابع ما مشتق پذیر در همهٔ نقاط است درحالیکه در حالت برشی مشتق پذیری از بین می رود.

{\displaystyle v\prime _{ij}(t+1)=tanh({\frac {v_{ij}(t+1)}{v_{maxj}}})v_{maxj}(t)}

در حالت برشی در نقطه برش مشتق پذیری از بین میرود؛ ولی در تانژانت هیپربولیک این مشکل وجود ندارد.

استفاده از ضریب کاهنده سرعت

در این روش از یک که باید بین ۰ و ۱ باشد، استفاده می‌شود. در غیر این صورت اگر بیش از ۱ باشد، سرعت افزاینده خواهد بود.

۱۴

حال تعیین کردن خود این یک مسئله است که روش‌های متعددی برای آن وجود دارد.

روش اول:

۱۵

روش دوم:

۱۶

در این روش باید را طوری تعیین کنیم که کمتر از ۱ شود.

روش سوم:

۱۷

برای می‌توان ۰٫۹ را انتخاب کرد و برای مقدار ۰٫۲ مناسب است.

روش چهارم:

در این روش براساس مقدار شایستگی تغییر می‌کند که این روش مناسب تری از روش‌های بالا است.

۱۸
۱۹

که شایستگی راه‌حل بهینه سراسری است.

روش پنجم:

در این روش که جدیدترین روش است یک پارامتر X خی که در کل سرعت ضرب می‌شود.

۲۰
۲۱ + ,
۲۲ ; k ∈[۰٬۱]
استفاده از تابعی برحسب تکرار برای سرعت بیشینه

در این روش سعی می‌شود تا از یک سرعت بیشینه به صورت تابعی از تکرار استفاده شود. در این روش سرعت بیشینه طبق رابطه ۱۳ برای هر بعد جداگانه حساب می‌شود.

[null 13]

این قابلیت باعث میشود که سرعت بیشینه با توجه به نیاز ما برای تنظیم وزن کاوش و انتفاع در هر تکرار مربوطه تعیین شود.

الگوریتم ازدحام ذرات آسنکرون

الگوریتم ازدحام ذرات گفته شده نوع سنکرون بود، یک نوع دیگری از این الگوریتم وجود دارد که به آن آسنکرون گویند. در این حالت پس از محاسبه شایستگی هریک از ذرات اگر بهینه سراسری عوض شد بلافاصله به‌روزرسانی میشود. این کار باعث میشود که سرعت همگرایی تا حدودی افزایش یابد؛ ولی امکان پیاده‌سازی موازی را می‌گیرد.

الگوریتم ازدحام ذرات تمام آگاه

ذرات با اطلاع هم و با هماهنگی حرکت می‌کنند و یک ذره حرکت بقیه را نیز در حرکت خودش دخالت می‌دهد.

۲۳

که یک عدد تصادفی است که به هر سرعت ذره یک وزن می‌دهد. در این حالت طبق رابطه هر ذره کاملاً کورکورانه بردار حرکت سایر ذرات را مد نظر قرار میدهد و با گرفتن برآیندی از جهت و اندازه حرکت همه، جهت و اندازه خود را بدست میآورد.

الگوریتم ازدحام ذرات استخوان خالی

این الگوریتم نسبت به الگوریتمهای قبلی خیلی سریع تر همگرا می‌شود و دارای دو نسخه است که در نسخه اول سرعت به شیوه زیر محاسبه می‌شود.

نسخه اول:

۲۴
۲۵
۲۶

نسخه دوم:

در این حالت در نیمی از حالات از روش استخوان خالی و در نیمی از الگوریتم ازدحام ذرات معمول استفاده میشود.

۲۷
الگوریتم ازدحام ذرات پیش کشیدن و پس زدن

در این الگوریتم ذره به سمت بهینه سراسری جذب می‌شود وقتی که به مقدار آستانه اول برسد، دفع می‌شود تا به جستجوی بیشتری بپردازد و این دفع شدن تا جایی ادامه پیدا میکند که به آستانه دوم برسد. عملیات جذب شدن با فرمول ۳۲ ولی دفع شدن با فرمول ۳۳ صورت می‌گیرد.

[null 32]
[null 33]
الگوریتم ازدحام ذرات شکار و شکارچی

هنگامی که ذرات همگرا شوند یک پارامتر ترس اعمال می‌کنیم با احتمال که باعث دور شدن ذرات از بهینه سراسری می‌شود و درنتیجه جستجو بیشتر می‌شود.

۳۴
۳۵
الگوریتم ازدحام ذرات چندشروعه

در این روش عملاً یک ذره را جهش می‌دهیم. برای وقتی است که مقدار شایستگی در چند تکرار بهبودی ندارد.

جمع‌بندی

الگوریتم ازدحام ذرات دودویی

این یکی از الگوریتم‌هایی است که برای مسائل جایگذاری یا عدم جایگذاری استفاده می‌شود. برای این کار باید از یک نگاشت برای تبدیل از پیوسته به دودویی استفاده شود.

منبع

بهینه‌سازی ازدحام ذرات (PSO) قسمت ۱
بهینه‌سازی ازدحام ذرات (PSO) قسمت ۲

انواع سامانه‌های توصیه‌گر

سامانه‌های توصیه‌گر به طور کلی به سه دسته تقسیم می‌شوند؛ در رایج‌ترین تقسیم‌بندی، آنها را به سه گروه ۱. محتوا محور ۲. دانش محور و ۳. صافی سازی تجمعی، تقسیم می‌کنند، که البته گونه چهارمی تحت عنوان Hybrid RS هم برای آنها قائل می‌شوند.

یک رویکرد به سیستم‌های توصیه‌گر، استفاده از الگوریتم‌های CF یا صافی سازی تجمعی است. در این رویکرد به جای استفاده از محتوای (Content) اقلام، از نظرات و رتبه‌بندی‌های انجام شده توسط کاربران برای ارائه پیشنهاد، استفاده می‌شود. مشکل اصلی استفاده از این رویکرد، مشکل شروع سرد (Cold Start problem)[۲] می‌باشد که برای کاربران جدید بروز می‌کند که در سیستم ثبت نام می‌کنند و سیستم هیچ اطلاعاتی از نظرات یا علایق کاربر ندارد (New User problem). در چنین شرایطی، سیستم‌ها معمولاً از یادگیری فعال (Active Learning)[۳] یا استفاده از ویژگی‌های شخصیتی کاربر،[۴] برای حل مشکل استفاده می‌کنند.

در روش محتوا محور، اقلام پیشنهادی، به این دلیل که با اقلامی که کاربر فعال (کاربری که قرار است به او توصیه کنیم) نسبت به آنها ابراز علاقه کرده‌است شباهت‌هایی دارند، به کاربر توصیه می‌شوند ولی در CF، لیست اقلام پیشنهادی، بر اساس این اصل که، کاربرانی، مشابه کاربر فعال، از آنها رضایت داشته‌اند تهیه می‌شود. از این رو واضح است که در روش محتوامحور، تمرکز بر روی یافتن شباهت بین اقلام بوده، در حالی که در CF، تمرکز روی یافتن شباهت بین کاربران است؛ بدین ترتیب که پیشنهادات در CF، بر اساس تشابه رفتاری کاربرفعال با کاربران دیگر صورت می‌گیرد و نه بر اساس تشابه ویژگی کالاهای پیشنهادی با ویژگی‌های کالاهای مورد علاقه وی (کاربر فعال). رویکرد محتوا محور یکی از روشهای مؤثر برای حلی نوعی از مشکل شروع سرد می‌باشد که برای کالاهای (آیتم‌های) جدید رخ می‌دهد (New Item problem)[۵] که به تازگی به لیست سیستم اضافه شده‌اند و هیچ کاربری در مورد آنها نظری نداده است. در چنین حالتی رویکرد صافی سازی تجمعی نمی‌تواند این کالاها را به کاربران توصیه کند.

اما گونه سوم این سیستم‌ها را با نام سیستم‌های دانش محور می‌شناسند. این سیستم‌ها براساس ادراکی که از نیازهای مشتری و ویژگی‌های کالاها پیدا کرده‌اند، توصیه‌هایی را ارائه می‌دهند. به عبارتی در این گونه از سیستم‌های توصیه‌گر مواد اولیه مورد استفاده برای تولید لیستی از پیشنهادها، دانش سیستم در مورد مشتری و کالا است. سیستم‌های دانش محور از متدهای مختلفی که برای تحلیل دانش، قابل استفاده هستند بهره می‌برند که متدهای رایج در الگوریتم‌های ژنتیک، فازی، شبکه‌های عصبی و … از جمله آنهاست. همچنین، در این گونه سیستم‌ها از درخت‌های تصمیم، استدلال نمونه‌محور و … نیز می‌توان استفاده کرد. یکی از رایج‌ترین متدهای تحلیل دانش درسیستم‌های توصیه‌گر دانش محور ،CBR یا روش استدلال نمونه‌محور است.

گونه چهارم سیستم‌های ترکیبی هستند. طراحان این نوع سیستم‌ها دو یا چند گونه از انواع سه‌گانه مذکور را غالباً به دو منظور با هم ترکیب می‌کنند؛ ۱- افزایش عملکرد سیستم ۲- کاهش اثر نقاط ضعفی که آن سیستم‌ها وقتی به تنهایی به کار گرفته شوند، دارند. از میان سه روش موجود (CF و CB و KB)، غالباً روش CF یک پای ثابت این ترکیبات است.

منبع


سیستم توصیه گر (Recommender Systems) چیست ؟

 

سیستم توصیه گر

 

 

سیستم توصیه گر

 

سیستم توصیه گر (Recommender System) قسمت ۱
سیستم توصیه گر (Recommender System) قسمت ۲
سیستم توصیه گر (Recommender System) قسمت ۳

۳-۵-انواع روش‌هاي فرا ابتكاري برگرفته از طبيعت

۱-۳-۵-الگوريتم ژنتيك

الگوريتم ژنتيك (Genetic Algorithm) روشي عمومي از روش‌هاي فرا ابتكاري براي بهينه‌سازي گسسته مي‌باشد كه مسائل جدول زمانبندي را حل مي‌نمايد. روش شبيه‌سازي كه در ادامه مورد بحث قرار می‌گيرد، راهبرد تكاملي نام دارد. اين روش در سال ۱۹۷۵ به وسيله هولند (Holland) و در سال ۱۹۸۹ توسط  گولدبرگ (Goldberg) ابداع شده است.

اين روش نوعي روش جستجوي همسايه است كه عملكردي مشابه ژن دارد. در طبيعت، فرايند تكامل هنگامي ايجاد مي‌شود كه چهار شرط زير برقرار باشد:

الف) يك موجود توانايي تكثير داشته باشد (قابليت توليد مثل).

ب) جمعيتي از اين موجودات قابل تكثير وجود داشته باشد.

پ) چنين وضعيتي داراي تنوع باشد.

ت) اين موجودات به وسيله قابليت‌هايي در زندگي از هم جدا شوند.

در طبيعت، گونه‌هاي متفاوتي از يك موجود وجود دارند كه اين تفاوت‌ها در كروموزوم‌هاي اين موجودات ظاهر مي‌شود و باعث تنوع در ساختار و رفتار اين موجودات مي‌شود.

اين تنوع ساختار و رفتار به نوبه خود بر زاد و ولد تأثير مي‌گذارد. موجوداتي كه قابليت‌ها و توانايي بيشتري براي انجام فعاليت‌ها در محيط دارند (موجودات متكامل‌تر)، داراي نرخ زاد و ولد بالاتري خواهند بود و طبعاً موجوداتي كه سازگاري كمتري با محيط دارند، از نرخ زاد و ولد پايين‌تري برخوردار خواهند بود. بعد از چند دوره زماني و گذشت چند نسل، جمعيت تمايل دارد كه موجوداتي را بيشتر در خود داشته باشد كه كروموزوم‌هايشان با محيط اطراف سازگاري بيشتري دارد.

در طي زمان، ساختار افراد جامعه به علت انتخاب طبيعي تغيير مي‌كند و اين نشانه تكامل جمعيت است.

۶- آنيلينگ شبيهسازي شده

اين روش توسط متروپوليس (Metropolis) و همكاران در سال ۱۹۵۳ پيشنهاد شده و جهت بهينه‌سازي به وسيله وچی (Vecchi)، گلات (Gelatt) و کرک‌پاتريک (kirkpatrick ) در سال ۱۹۸۳ مورد بازبيني قرار گرفته است. اين روش در مسائل تاكسي تلفني كاربرد دارد.

الگوريتم آنيلينگ شبيه‌سازي شده (Simulated Annealing) در شکل عمومي، بر اساس شباهت ميان سرد شدن جامدات مذاب و حل مسائل بهينه‌سازي تركيبي به وجود آمده است. در فيزيك مواد فشرده، گرم و سرد كردن فرايندي است فيزيكي كه طي آن يك ماده جامد در ظرفي حرارت داده مي‌شود تا مايع شود؛ سپس حرارت آن بتدريج كاهش مي‌يابد. بدين ترتيب تمام ذرات فرصت مي‌يابند تا خود را در پايين‌ترين سطح انرژي منظم كنند. چنين وضعي در شرايطي ايجاد مي‌شود كه گرمادهي كافي بوده و سرد كردن نيز به آهستگي صورت گيرد. جواب حاصل از الگوريتم گرم و سرد كردن شبيه‌سازي شده، به جواب اوليه وابسته نيست و مي‌توان توسط آن جوابي نزديك به جواب بهينه به دست آورد. حد بالايي زمان اجراي الگوريتم نيز قابل تعيين است. بنابراين الگوريتم گرم و سرد كردن شبيه‌سازي شده، الگوريتمي است تكراري كه اشكالات روش‌هاي عمومي مبتني بر تكرار را ندارد.

در روش آنيلينگ شبيه‌سازي شده، به صورت پي در پي از جواب جاري به يكي از همسايه‌هاي آن انتقال صورت مي‌گيرد. اين سازوکار توسط زنجيره ماركوف به صورت رياضي قابل توصيف است. در اين روش، يك مجموعه آزمون انجام مي‌گيرد؛ اين آزمون‌ها به نحوي است كه نتيجه هر يك به نتيجه آزمون قبل وابسته است. در روش آنيلينگ شبيه‌سازي شده، منظور از يك آزمون، انتقال به نقطه جديد است و روشن است كه نتيجه انتقال به نقطه جديد تنها وابسته به مشخصات جواب جاري است.

روش جستجوي همسايه و روش آنيلينگ شبيه‌سازي شده، هر دو روش‌هاي تكراري هستند. در الگوريتم آنيلينگ شبيه‌سازي شده، هر بار كه شاخص كنترل‌كننده به مقدار نهايي خود مي‌رسد، در حقيقت يك عمليات تكراري انجام شده است. در الگوريتم جستجوي همسايه، هنگامي كه تعداد تكرارها به سمت بي‌نهايت ميل مي‌كند، روش به جواب بهينه نزديك مي‌شود. اما عملكرد الگوريتم آنيلينگ شبيه‌سازي شده سريع‌تر است.

۷-شبکه‌های عصبی

شبکه‌های عصبی (Neural Networks) مصنوعی سيستم‌هاي هوشمندی هستند که از شبکه‌هاي عصبي طبيعي الهام گرفته شده‌اند. شبکه‌هاي عصبي مصنوعي در واقع تلاشي براي حرکت از سمت مدل محاسباتي فون نيومن به سمت مدلي است که با توجه به عملکرد و ويژگي‌هاي مغز انسان طراحي شده است. مدل فون نيومن گرچه هم اکنون بسيار استفاده می‌شود، اما از کمبودهايي رنج مي‌برد که تلاش شده است اين کمبودها در شبکه‌هاي عصبي مصنوعي برطرف شود.

در سال ۱۹۴۳ مدلی راجع به عملکرد نورون‌ها (Neuron) نوشته شد که با اندکی تغيير، امروزه بلوک اصلی سازنده اکثر شبکه‌های عصبی مصنوعی می‌باشد. عملکرد اساسی اين مدل مبتنی بر جمع کردن ورودی‌ها و به دنبال آن به وجود آمدن يک خروجی است. ورودی‌های نورون‌ها از طريق دنريت‌ها که به خروجی نورون‌های ديگر از طريق سيناپس متصل است، وارد می‌شوند. بدنه سلولی کليه اين ورودی‌ها را دريافت می‌کند و چنانچه جمع اين مقادير از مقداری که به آن آستانه گفته می‌شود بيشتر باشد، در اصطلاح برانگيخته شده يا آتش می‌کند و در اين صورت، خروجی نورون روشن يا خاموش خواهد شد.

مدل پایه ای نرون

مدل پايه‌اي نورون به صورت شکل ۱-۳ تعريف می‌شود.

مدل پایه ای نرون

مدل پایه ای نرون

اين مدل و برخی از کارهای مربوط به آن را بايد به عنوان شروع مطالعات تحليلی در نظر گرفت. گر چه اين مدل ساده فاقد ويژگی‌های پيچيده بدنه سلولی نورون‌های بيولوژيکی است، اما می‌توان به آن به صورت مدلی ساده از آنچه که واقعاً موجود است نگريست.

جستجوي ممنوع

روشی عمومي است كه به وسيله گلوور (Glover) در سال ۱۹۸۹ پيشنهاد شده و در حل مسائل برنامه‌ريزي كاري ـ خريد کاربرد دارد.

روش جستجوي ممنوع (Tabu Search)، همانند روش آنيلينگ شبيه‌سازي شده بر اساس جستجوي همسايه بنا شده است. در اين روش عملكرد حافظه انسان شبيه‌سازي شده است. حافظه انسان با به كارگيري ساختماني مؤثر و در عين حال ساده از اطلاعات، آنچه را در قبل رؤيت شده، ذخيره مي‌كند. اين مركز همچنين فهرستی از حركات منع شده را تنظيم مي‌كند و اين فهرست همواره بر اساس آخرين جستجوها منظم مي‌شود. اين روش از انجام هر گونه عمليات مجدد و تكراري جلوگيري مي‌كند.

شكل نوين جستجوي ممنوع توسط گلوور مطرح شده است. روش جستجوي مبتني بر منع، با ايجاد تغييري كوچك در روش جستجوي همسايه به وجود مي‌آيد. هدف اين روش آن است كه بخش‌هايي از مجموعه جواب كه پيش از اين بررسي نشده است، مد نظر قرار گيرد. بدين منظور حركت به جواب‌هايي كه اخيراً جستجو شده، ممنوع خواهد بود.

ساختار كلي روش جستجوي ممنوع بدين صورت است كه ابتدا يك جواب اوليه امكان‌پذير انتخاب مي‌شود؛ سپس براي جواب مربوط، بر اساس يک معيار خاص مجموعه‌اي از جواب‌هاي همسايه امكان‌پذير در نظر گرفته مي‌شود.

در گام بعد، پس از ارزيابي جواب‌هاي همسايه تعيين شده، بهترين آنها انتخاب مي‌شود و جابه‌جايي از جواب جاري به جواب همسايه انتخابي صورت مي‌گيرد. اين فرايند به همين ترتيب تكرار مي‌شود تا زماني كه شرط خاتمه تحقق يابد.

در روش جستجوي ممنوع، فهرستي وجود دارد كه جابه‌جايي‌هاي منع شده را نگهداري مي‌كند و به فهرست تابو معروف است و كاربرد اصلي آن، پرهيز از همگرا شدن به جواب‌هاي بهينه محلي است. به عبارت ديگر، به كمك فهرست تابو جابه‌جايي به جواب‌هايي كه اخيراً جستجو شده‌اند، ممنوع خواهد شد. فقط بخش‌هايي از مجموعه جواب كه پيش از اين مورد بررسي قرار نگرفته، مد نظر خواهند بود. در واقع جابه‌جايي از جواب جاري به جواب همسايه امكان‌پذير زماني انجام مي‌شود كه در فهرست تابو قرار نداشته باشد. در غير اين صورت، جواب همسايه ديگري كه در ارزيابي جواب‌هاي همسايه در رده بعدي قرار گرفته است، انتخاب شده و جابه‌جايي به آن صورت مي‌گيرد.

در روش جستجوي ممنوع بعد از هر جابه‌جايي، فهرست تابو بهنگام مي‌شود، به نحوي كه جابه‌جايي جديد به آن فهرست اضافه شده و جابه‌جايي كه تا n  تكرار مشخص در فهرست بوده است، از آن حذف مي‌شود. نحوه انتخاب مي‌تواند با توجه به شرايط و نوع مسأله متفاوت باشد.

سيستم مورچه (Ant System)

اين روش در سال ۱۹۹۱ توسط مانيه‌زو (Maniezzo) دوريگو (Dorigo) و کولورنی (Colorni) پيشنهاد شده است كه در حل مسأله فروشنده دوره‌گرد و مسائل تخصيص چندوجهي کاربرد دارد.

الگوريتم بهينه‌سازي كلوني مورچه‌ها از عامل‌هاي ساده‌اي كه مورچه ناميده مي‌شوند، استفاده مي‌كند تا به صورت تكراري جواب‌هايي توليد كند. مورچه‌ها می‌توانند كوتاه‌ترين مسير از يك منبع غذايي به لانه را با بهره‌گيري از اطلاعات فرموني پيدا کنند. مورچه‌ها در هنگام راه رفتن، روي زمين فرمون مي‌ريزند و با بو كشيدن فرمون ريخته شده بر روي زمين راه را دنبال مي‌كنند؛ چنانچه در طي مسير به سوي لانه به يك دوراهي برسند، از آن جايي كه هيچ اطلاعي درباره راه بهتر ندارند، راه را به تصادف برمي‌گزينند. انتظار مي‌رود به طور متوسط نيمي از مورچه‌ها مسير اول و نيمي ديگر مسير دوم را انتخاب كنند.

فرض مي‌شود كه تمام مورچه‌ها با سرعت يكسان مسير را طي كنند. از آنجا كه يك مسير كوتاه‌تر از مسير ديگر است، مورچه‌هاي بيشتري از آن مي‌گذرند و فرمون بيشتري بر روي آن انباشته مي‌شود. بعد از مدت كوتاهي مقدار فرمون روي دو مسير به اندازه‌اي مي رسد كه روي تصميم مورچه‌هاي جديد براي انتخاب مسير بهتر تأثير مي‌گذارد. از اين به بعد، مورچه‌هاي جديد با احتمال بيشتري ترجيح مي‌دهند از مسير كوتاه‌تر استفاده كنند، زيرا در نقطه تصميم‌گيري مقدار فرمون بيشتري در مسير كوتاه‌تر مشاهده مي‌كنند. بعد از مدت كوتاهي تمام مورچه‌ها اين مسير را انتخاب خواهند كرد.

منبع


مقدمه ای بر بهینه سازی و الگوریتم های موجود

هدف از بهينه‌سازي يافتن بهترين جواب قابل قبول، با توجه به محدوديت‌ها و نيازهاي مسأله است. براي يك مسأله، ممكن است جواب‌هاي مختلفي موجود باشد كه براي مقايسه آنها و انتخاب جواب بهينه، تابعي به نام تابع هدف تعريف مي‌شود. انتخاب اين تابع به طبيعت مسأله وابسته است. به عنوان مثال، زمان سفر يا هزينه از جمله اهداف رايج بهينه‌سازي شبكه‌هاي حمل و نقل مي‌باشد. به هر حال، انتخاب تابع هدف مناسب يكي از مهمترين گام‌هاي بهينه‌سازي است. گاهي در بهينه‌سازي چند هدف  به طور همزمان مد نظر قرار مي‌گيرد؛ اين گونه مسائل بهينه‌سازي را كه دربرگيرنده چند تابع هدف هستند، مسائل چند هدفي مي‌نامند. ساده‌ترين راه در برخورد با اين گونه مسائل، تشكيل يك تابع هدف جديد به صورت تركيب خطي توابع هدف اصلي است كه در اين تركيب ميزان اثرگذاري هر تابع با وزن اختصاص يافته به آن مشخص مي‌شود. هر مسأله بهينه‌سازي داراي تعدادي متغير مستقل است كه آنها را متغيرهاي طراحي می‌نامند كه با بردار n  بعدي x  نشان داده مي‌شوند.

هدف از بهينه‌سازي تعيين متغيرهاي طراحي است، به گونه‌اي كه تابع هدف كمينه يا بيشينه شود.

مسائل مختلف بهينه‌سازي  به دو دسته زير تقسيم مي‌شود:

الف) مسائل بهينه‌سازي بي‌محدوديت: در اين مسائل هدف، بيشينه يا كمينه كردن تابع هدف بدون هر گونه محدوديتي بر روي متغيرهاي طراحي مي‌باشد.

ب) مسائل بهينه‌سازي با محدوديت: بهينه‌سازي در اغلب مسائل كاربردي، با توجه به محدوديت‌هايي صورت مي‌گيرد؛ محدوديت‌هايي كه در زمينه رفتار و عملكرد يك سيستم مي‌باشد و محدوديت‌هاي رفتاري و محدوديت‌هايي كه در فيزيك و هندسه مسأله وجود دارد، محدوديت‌هاي هندسي يا جانبي ناميده مي‌شوند.

معادلات معرف محدوديت‌ها ممكن است  به صورت مساوي يا نامساوي باشند كه در هر مورد، روش بهينه‌سازي متفاوت مي‌باشد. به هر حال محدوديت‌ها، ناحيه قابل قبول در طراحي را معين مي‌كنند.

فرايند بهينه ­سازي

فرموله كردن مسئله: در اين مرحله، يك مسئله ­ي تصميم ­گيري، همراه با يك ساختار كلي از آن تعريف مي‌شود. اين ساختار كلي ممكن است خيلي دقيق نباشد اما وضعيت كلي مسئله را، كه شامل فاكتورهاي ورودي و خروجي و اهداف مسئله است، بيان مي­ كند. شفاف­ سازي و ساختاردهي به مسئله، ممكن است براي بسياري از مسايل بهينه­ سازي، كاري پيچيده باشد.

مدل­ سازي مسئله:

در اين مرحله يك مدل رياضي كلي براي مسئله، ساخته مي­ شود. مدل­سازي ممكن است از مدل­ هاي مشابه در پيشينه ­ي موضوع كمك بگيرد. اين گام موجب تجزيه مسئله به يك يا چند مدل بهينه‌سازي مي­ گردد.

بهينه­ سازي مسئله:

پس از مدل سازي مسئله، روال حل، يك راه ­حل خوب براي مسئله توليد مي­ كند. اين راه‌حل ممكن است بهينه يا تقريباً بهينه باشد. نكته ­اي كه بايد به آن توجه داشت اين است كه راه ­حل به دست آمده، راه­ حلي براي مدل طراحي شده است، نه براي مسئله ­ي واقعي. در هنگام فرموله كردن و مدلسازي ممكن است تغييراتي در مسئله واقعي به وجود آمده و مسئله­ ي جديد، نسبت به مسئله­ ي واقعي تفاوت زيادي داشته باشد.

استقرار مسئله:

راه ­حل به دست آمده توسط تصميم گيرنده بررسي مي­ شود و در صورتي كه قابل قبول باشد، مورد استفاده قرار مي­ گيرد و در صورتي كه راه­حل قابل قبول نباشد، مدل يا الگوريتم بهينه­ سازي بايد توسعه داده شده و فرايند بهينه­ سازي تكرار گردد.

الگوریتم­ های بهینه­ سازی

هدف الگوریتم­ های اکتشافی، ارائه راه حل در چارچوب یک زمان قابل قبول است که برای حل مسئله مناسب باشد، ممکن است الگوریتم اکتشافی، بهترین راه حل واقعی برای حل مسئله نبوده ولی می­ تواند راه حل نزدیک به بهترین باشد. الگوریتم ­های اکتشافی با الگوریتم­ های بهینه­ سازی برای اصلاح کارایی الگوریتم می‌توانند ترکیب شوند. الگوریتم فرا اکتشافی ترکیبی است از الگوریتم ­های اکتشافی که برای پیدا کردن، تولید یا انتخاب هر اکتشاف در هر مرحله طراحی می­شود و راه حل خوبی برای مسائلی که مشکل بهینه‌سازی دارند ارائه می‌دهد. الگوریتم­ های فرا اکتشافی برخی از فرضیات مسائل بهینه­ سازی که باید حل شود را در نظر می ­گیرد.

 روش‌هاي فرا ابتكاري برگرفته از طبيعت

الگوريتم ­هاي فراابتكاري الگوريتم ­هايي هستند كه با الهام از طبيعت، فيزيك و انسان طراحي شده ­اند و در حل بسياري از مسايل بهينه­ سازي استفاده می­ شوند. معمولاً از الگوريتم­ هاي فراابتكاري در تركيب با ساير الگوريتم­ ها، جهت رسيدن به جواب بهينه يا خروج از وضعيت جواب بهينه محلي استفاده مي­گردد. در سال‌هاي اخير يكي از مهمترين و اميدبخش‌ترين تحقيقات، «روش‌هاي ابتكاري برگرفته از طبيعت» بوده است؛ اين روش‌ها شباهت‌هايي با سيستم‌هاي اجتماعي و يا طبيعي دارند. كاربرد ‌آنها برگرفته از روش‌هاي ابتكاري پيوسته می‌باشد كه در حل مسائل مشكل تركيبي (NP-Hard) نتايج بسيار خوبی داشته است.

در ابتدا با تعريفي از طبيعت و طبيعي بودن روش‌ها شروع مي‌كنيم؛ روش‌ها برگرفته از فيزيك، زيست‌شناسي و جامعه‌شناسي هستند و به صورت زير تشكيل شده‌اند:

–         استفاده از تعداد مشخصي از سعي‌ها و كوشش‌هاي تكراري

–          استفاده از يك يا چند عامل (نرون، خرده‌ريز، كروموزوم، مورچه و غيره)

–          عمليات (در حالت چند عاملي) با يك سازوکار همكاري ـ رقابت

–          ايجاد روش‌هاي خود تغييري و خود تبديلي

طبيعت داراي دو تدبير بزرگ مي‌باشد:

۱-    انتخاب پاداش براي خصوصيات فردي قوي و جزا براي فرد ضعيف‌تر؛

۲-     جهش كه معرفي اعضای تصادفي و امکان تولد فرد جديد را ميسر می‌سازد.

به طور كلي دو وضعيت وجود دارد كه در روش‌هاي ابتكاري برگرفته از طبيعت ديده مي‌شود، يكي انتخاب و ديگري جهش. انتخاب ايده‌اي مبنا براي بهينه‌سازي و جهش ايده‌اي مبنا براي جستجوي پيوسته مي‌باشد.

از خصوصيات روش‌هاي ابتكاري  برگرفته از طبيعت، مي‌توان به موارد زير اشاره كرد:

۱-    پديده‌اي حقيقي در طبيعت را مدل‌سازي مي‌كنند.

۲-     بدون قطع مي‌باشند.

۳-     اغلب بدون شرط تركيبي همانند (عامل‌هاي متعدد) را معرفي مي‌نمايند.

۴-     تطبيق‌پذير هستند.

خصوصيات بالا باعث رفتاري معقول در جهت تأمين هوشمندي مي‌شود. تعريف هوشمندي نيز عبارت است از قدرت حل مسائل مشكل؛ بنابراين هوشمندي به حلمناسب مسائل بهينه‌سازي تركيبي منجر می‌شود.

برخی الگوریتم های بهینه سازی مهم عبارت انداز:

 الگوریتم بهینه سازی ازدحام ذرات

در سال ۱۹۹۵ ابرهارت و کنیدی براي اولین بار PSOبه عنوان یک روش جستجوي غیر قطعی براي بهینه سازي تابعی مطرح گشت این الگوریتم از حرکت دسته جمعیپرندگانی که به دنبال غذا می­باشند، الهام گرفته شده است. گروهي از پرندگان در فضايي به صورت تصادفي دنبال غذا مي­گردند. تنها يك تكه غذا در فضاي مورد بحث وجود دارد. هيچ يك از پرندگان محل غذا را نمي­دانند. يكي از بهترين استراتژي­ها مي­تواند دنبال كردن پرنده­اي باشد كه كمترين فاصله را تا غذا داشته باشد.  ايناستراتژي در واقع جانمايه الگوريتم است. هر راه­حل كه به آن يك ذره گفته مي­شود،PSO  در الگوريتم معادل يك پرنده در الگوريتم حركت جمعي پرندگان مي­باشد.هر ذره يك مقدار شايستگي دارد كه توسط يك تابع شايستگي محاسبه مي­شود. هر چه ذره در فضا­ي جستجو به هدف – غذا در مدل حركت پرندگان-  نزدیكتر باشد،شايستگي بيشتري دارد. همچنين هر ذره داراي يك سرعت است كه هدايت حركت ذره را بر عهده دارد. هرذره با دنبال كردن ذرات بهينه در حالت فعلي، به حركت خوددر فضاي مساله ادامه مي­دهد.

الگوریتم کرم شب­ تاب

الگوریتم کرم شب ­تاب به عنوان الگوریتم ذهنی مبتنی بر ازدحام، برای وظایف بهینه­سازی محدود، ارائه شده است. در این الگوریتم از رفتار تابشی کرم­های شب تابالهام گرفته شده است. کرم­های شب تاب در طبیعت به طور دسته جمعی زندگی می­کنند و همواره کرم کم نورتر به سمت کرم پرنور تر حرکت می‌کند. این الگوریتم یکرویه تکراری مبتنی بر جمعیت را با عوامل بی­شمار (تحت عنوان کرم­های شب­تاب) به کار می‌گیرد. به این عوامل امکان داده می­شود تا فضای تابع هزینه را به صورتموثرتری نسبت به جستجوی تصادفی توزیع شده، بررسی کنند. تکنیک بهینه­سازی هوشمند، مبتنی بر این فرضیه است که راه­حل یک مشکل بهینه­سازی را، می­توان بهعنوان عاملی (کرم شب­تاب) در نظر گرفت که به صورت متناسب با کیفیت آن در یک محیط تابیده می­شود. متعاقباً هر کرم شب­تاب، همتایان خود را (صرف نظر ازجنسیتشان) جذب می­کند که فضای جستجو را به صورت موثرتری بررسی می­کند. الگوریتم­های کرم شب­تاب نورهای ریتمیک و کوتاه تولید می­کنند. الگوی نوری هر کدام از کرم­های شب­تاب با یکدیگر متفاوت می­باشند. الگوریتم­های کرم شب­تاب از این نورها به دو منظور استفاده می­کنند. ۱- پروسه جذب جفت­ها ۲- جذب شکار. به علاوه این نورها می­توانند به عنوان یک مکانیزم محافظتی برای کرم­های شب­تاب باشند.

الگوریتم کلونی مورچگان

بهینه‌سازی گروه مورچه‌ها یا ACO همانطور که می دانیم مسئله یافتن کوتاهترین مسیر، یک مسئله بهینه سازیست که گاه حل آن بسیار دشوار است و گاه نیز بسیار زمانبر. برای مثال مسئله فروشنده دوره گرد را نیز می‌توان مطرح کرد. در این روش(ACo)، مورچه‌های مصنوعی به‌وسیلهٔ حرکت بر روی نمودار مسئله و با باقی گذاشتن نشانه‌هایی بر روی نمودار، همچون مورچه‌های واقعی که در مسیر حرکت خود نشانه‌های باقی می‌گذارند، باعث می‌شوند که مورچه‌های مصنوعی بعدی بتوانند راه‌حل‌های بهتری را برای مسئله فراهم نمایند. همچنین در این روش می‌توان توسط مسائل محاسباتی-عددی بر مبنای علم احتمالات بهترین مسیر را در یک نمودار یافت.

 الگوریتم ژنتیک

الگوریتم‌های ژنتیک، تکنیک جستجویی در علم رایانه برای یافتن راه‌حل تقریبی برای بهینه‌سازی و مسائل جستجو است. الگوریتم ژنتیک نوع خاصی از الگوریتم‌های تکامل است که از تکنیک‌های زیست‌شناسی فرگشتی مانند وراثت و جهش استفاده می‌کند. این الگوریتم برای اولین بار توسط جان هنری هالند معرفی شد. در واقع الگوریتم‌های ژنتیک از اصول انتخاب طبیعی داروین برای یافتن فرمول بهینه جهت پیش‌بینی یا تطبیق الگو استفاده می‌کنند. الگوریتم‌های ژنتیک اغلب گزینه خوبی برای تکنیک‌های پیش‌بینی بر مبنای رگرسیون هستند. در هوش مصنوعی الگوریتم ژنتیک (یا GA) یک تکنیک برنامه‌نویسی است که از تکامل ژنتیکی به عنوان یک الگوی حل مسئله استفاده می‌کند. مسئله‌ای که باید حل شود دارای ورودی‌هایی می‌باشد که طی یک فرایند الگوبرداری شده از تکامل ژنتیکی به راه‌حلها تبدیل می‌شود سپس راه حلها بعنوان کاندیداها توسط تابع ارزیاب (Fitness Function) مورد ارزیابی قرار می‌گیرند و چنانچه شرط خروج مسئله فراهم شده باشد الگوریتم خاتمه می‌یابد. الگوریتم ژنتیک بطور کلی یک الگوریتم مبتنی بر تکرار است که اغلب بخش‌های آن به صورت فرایندهای تصادفی انتخاب می‌شوند.

منبع
بهينه‌سازی و معرفي روش‌های آن قسمت ۱
بهينه‌سازی و معرفي روش‌های آن قسمت ۲
بهينه‌سازی و معرفي روش‌های آن قسمت ۳

برگه‌ها

شرکت بهسان اندیش سپهر

طراح و مجری سامانه های کنترل تردد خودرو