بایگانی برچسب برای: ازمونیار

کار با Thread ها در زبان سی شارپ – آشنایی با Thread های Foreground و Background در دات نت

زمانی که یک Thread جدید در برنامه های دات نت ایجاد می شوند، این Thread ها می توانند به دو صورت Foreground و Background اجرا شوند:

  1. Thread های Foreground: زمانی که کی Thread در حالت Foreground اجرا می شود باعث می شود که Thread اصلی برنامه تا زمان کامل شدن اجرای Thread ایجاد شده در حالت اجرا بماند. یعنی از Shut-down شدن Primary Thread توسط CLR جلوگیری می شود.
  2. Thread های Background: این Thread ها که با نام Daemon Thread شناخته می شوند به CLR می گوید که اجرای این Thread آنقدر اهمیت ندارد که Thread اصلی برنامه بخواهد منتظر بماند تا عملیات آن به اتمام برسد و می تواند در هر زمان که Thread اصلی برنامه به اتمام رسید، به صورت خودکار Thread های Background را نیز از بین ببرد.

توجه کنید که کلیه Thread هایی که در برنامه ها ایجاد می کنیم به صورت پیش فرض در حالت Foreground قرار دارند. برای آشنایی بیشتر با این موضوع نمونه کد زیر را در نظر بگیرید:

static void Main(string[] args)
{
    var thread = new Thread(PrintNumbers);
    thread.Start();
}

public static void PrintNumbers()
{
    for (int counter = 1; counter < 10; counter++)
    {
        Console.WriteLine(counter);
        Thread.Sleep(200);
    }
}

همانطور که گفتیم Thread ایجاد شده به صورت پیش فرض از نوع Foreground است و به همین دلیل تا زمانی که روند اجرای Thread ایجاد شده به اتمام نرسد از برنامه خارج نمی شویم و کلیه اعداد در خروجی چاپ می شوند. اما در کد زیر Thread ایجاد شده به صورت Background است و خواهیم دید که پس از اجرای برنامه به دلیل اینکه Thread اصلی زودتر از Thread ایجاد شده به اتمام می رسد، CLR به صورت خودکار Thread ایجاد شده را از بین می برد و اعداد به صورت کامل در خروجی نمایش داده نمی شوند:

static void Main(string[] args)
{
    var thread = new Thread(PrintNumbers);
    thread.IsBackground = true;
    thread.Start();
}

public static void PrintNumbers()
{
    for (int counter = 1; counter < 10; counter++)
    {
        Console.WriteLine(counter);
        Thread.Sleep(200);
    }
}

در برنامه های واقعی باید با دقت نوع Thread ها را انتخاب کرد، برای مثال فرض کنید که در برنامه شما در یک Thread جداگانه عملیاتی بر روی داده های بانک اطلاعاتی انجام می شود و نتیجه این عملیات در انتها باید در جایی ذخیره شود، می توانید برای اینکار یک Thread از نوع Foreground ایجاد کرده تا پس از خروج از برنامه، Thread اصلی منتظر اتمام انجام عملیات شده و سپس عملیات خروج کامل انجام شود. در مبحث بعدی در مورد موضوع همزمانی یا Concurrency صحبت می کنیم که از مشکلات اساسی در زمینه برنامه نویسی asynchronous می باشد و در مورد راهکار های حل این مشکل نیز صحبت خواهیم کرد.

منبع


قسمت اول آموزش-برنامه نویسی 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 به صورت همزمان در سی شارپ

مقدمه

حذف نویز تصاویر _ گروهی از محققان سیستمی را توسعه داده اند که با استفاده از هوش مصنوعی و بدون نیاز به عکس های واضح از منبع، نویز تصاویر را از بین می برد.

شرح خبر

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

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

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

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

راه اندازی کانال شرکت بهسان اندیش در تلگرام و سایر پیام رسان ها

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

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

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

? پیام رسان تلگرام (مرکز اصلی اطلاع رسانی شرکت) :

https://t.me/Behsan_Transit

 

? در سروش (با اینترنت نیم بها) :
sapp.ir/behsanandish

 

 

 

مرحله 2: پیدا کردن قدرت و جهت گرادیان لبه.

گام بعدی استفاده از Mask های Sobel برای پیدا کردن قدرت و جهت گرادیان لبه برای هر پیکسل است. ابتدا ماسک های Sobel به محدوده پیکسل 3×3 پیکسل فعلی در هر دو جهت x و y اعمال می شود. سپس مجموع مقدار هر ماسک ضربدر پیکسل مربوطه به ترتیب به عنوان مقادیر Gx و Gy محاسبه می شود. ریشه دوم مربع Gx به اضافه Gy مربع برابر قدرت لبه است. Tangent معکوس Gx / Gy جهت لبه را تولید می کند. سپس جهت لبه تقریب شده است به یکی از چهار مقادیر ممکن که ایجاد می کند جهت های ممکن را که  یک لبه می تواند در یک تصویر از یک شبکه پیکسل مربع باشد. این جهت لبه در edgeDir [row] [col] ذخیره می شود و قدرت گرادیان در  array gradient[row] [col] ذخیره می شود.

 

CannyEdgeWeel

هر زاویه لبه در 11.25 درجه از یکی از  زاویه های ممکن به آن مقدار تغییر می کند.

 

#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	10

	// 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
		0,											// Row position to display bitmap in videoportal
		0,											// 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:
	// (1) grayScaleTheFrameData - which mallocs m_destinationBmp
	// (2) doMyImageProcesing
	// (3) 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 در سی پلاس پلاس قسمت 1
الگوریتم Canny در سی پلاس پلاس قسمت 2
الگوریتم Canny در سی پلاس پلاس قسمت 3
الگوریتم Canny در سی پلاس پلاس قسمت 4

عامل های هوشمند در هوش مصنوعی

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

عامل ها و محیط ها

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

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

تأکید میکنیم که فرضیه ی عامل، ابزاری برای تحلیل سیسیتم هاست، نه یک ویژگی خاص برای تقسیم دنیا به عامل و غیر عامل ها. ماشین حساب را میتوان عاملی در نظر گرفت که با توجه به “دنباله ادراک” ، “=2+2” نماد “4” را نمایش میدهد. اما این تحلیل منجر به درک ماشین حساب نمیشود. از جهاتی تمام حوزه های مهندسی را می توان طراحی کننده ی محصولات مصنوعی دانست که با دنیا تعامل دارند؛ AI (از نظر مؤلفین) در انتهای این طیف واقع است، که در آنجا، محصولات مصنوعی، منابع محاسباتی ارزشمندی دارند و محیط کار ، نیازمند تصمیم گیری مهم است.

رفتار خوب : مفهوم خردمندی یا عقلانیت

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

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

روشن است برای تمام کارها و عامل ها، یک معیار کارایی ثابت وجود ندارد؛ معمولا طراح، یک معیار را برای شرایط خاصی ، مناسب می بیند. این کار، چندان آسان نیست.

• خردمندی یا عقلانیت (rationality)

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

1. معیار کارایی که ملاک های موفقیت را تعریف می کند.

2. دانش قبلی عامل نسبت به محیط.

3. فعالیتهایی که عامل میتواند انجام دهد.

4. دنباله ی ادراک عامل در این زمان.

به این ترتیب میتوانیم عامل خردمند را بصورت زیر تعریف کنیم:

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

• همه چیزدانی، یادگیری و خود مختاری

باید بین “همه چیزدانی” و خردمندی تمایز قائل شویم. عامل “همه چیزدان”، خروجی واقعی فعالیت های خودش را می داند و می تواند بر اساس آن عمل کند. اما “همه چیزدان” بودن عامل، غیرممکن است. این مثال را در نظر بگیرید: من روزی در حال قدم زدن با احمد هستم و دوست قدیمی خود را در آن طرف خیابان می بینم. هیچ ترافیکی وجود ندارد و “خردمندی” حکم میکند که در عرض خیابان حرکت کنم. در همین حال، در فاصله 33000 فوتی، یک هواپیمای مسافری با هواپیمای باری برخورد میکند و قبل از اینکه به آن طرف خیابان بروم، نقش بر زمین میشوم. آیا عبور از عرض خیابان، عقلایی (خردمندی) نبود؟ بعید است کسی بگوید که عبور از عرض خیابان حماقت بوده است.

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

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

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

منبع


منابع

1.https://fa.wikipedia.org

2.http://retro-code.ir

3.http://www.sourcecodes.ir

 

 

عامل های هدف گرا

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

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

برای مثال فرض کنید که راننده هوشمند قصد دارد تا مسیری را از میان خیابان های تو در تو یک شهر بزرگ برای رسیدن به مقصد  انتخاب نماید. (Google Map یک مثال کاربردی آن میباشد) آیا این کار به سادگی انتخاب یک واکنش خواهد بود؟! نکته ی مهمی که باید به آن توجه داشت این است که در عامل های هدف گرا مکانیزم تصمیم گیری شباهتی به مکانیزم استراتژی واکنشی ساده ندارد. در این عامل ها باید همواره به این دو سوال جواب داد.

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

عامل های مبتنی بر سودمندی

گاهی اوقات تنها رسیدن به هدف نیست که در افزایش بازدهی عامل هوشمند موثر است. برای مثال فرض کنید میخواهیم از تهران به چالوس سفر کنیم. برای این سفر انتخاب های متعددی میتواند توسط راننده هوشمند انجام شود. اگر قرار باشد راننده هوشمند در محاسبات خود تنها به رسین به هدف توجه کند ممکن است در طول سفر خود از زاهدان عبور نماییم! دلیل این اتفاق کاملا واضح است. در این مسئله برای عامل هیچ اهمیتی ندارد که کدام مسیر را انتخاب نماید!

تنها موردی که برای عامل اهمپیت دارد رسیدن به هدف است! بدین ترتیب در این مثال سفر 5 ساعته ما به 50 ساعت افزایش پیدا خواهد کرد و عملا هوشمندی عامل مورد نظر شکست خورده و فاقد ارزش میباشد. (در مطالبی که مرتبط با الگورتیم های جستجوی هدف میباشند این مشکل را به وضوح مشاهده خواهید کرد.) پس میتوان نتیجه گیری نمود که  در مسئله فوق عواملی جز هدف وجود دارند که در استراتژی انتخاب واکنش ها تتاثیر چشم گیری میگذارند.

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

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

منبع : http://retro-code.ir


ساختار عامل های هوشمند

کار AI طراحی برنامه ی عامل است که “تابع عامل” را پیاده سازی می کند. تابع عامل، ادراکات را به فعالیت ها نگاشت می کند. فرض میکنیم این برنامه بر روی یک دستگاه محاسباتی با حسگرها و محرک های فیزیکی، یعنی معماری اجرا می شود: برنامه + معماری = عامل

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

♦ برنامه های عامل  (agent programs)

برنامه های عامل درک فعلی را به عنوان ورودی از حسگرها (سنسورها) می پذیرند، و فعالیت را از طریق محرک ها انجام می دهند. توجه داشته باشید که ، برنامه عامل درک فعلی را به عنوان ورودی می گیرد، ولی تابع عامل کل سابقه درک را دریافت میکند. برنامه ی عامل، فقط درک فعلی را به عنوان ورودی می پذیرد، زیرا هیچ چیز دیگری از محیط در دسترس نیست. اگر فعالیت های عامل ، به کل “دنباله ی ادراک” بستگی داشته باشد، عامل باید کل ادراک ها را به یاد بیاورد.

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

function TABLE-DRIVEN-AGENT (percept) returns an action
  presistent: percepts, a sequence, initially empty table, a table of actions, indexed by percept sequences, initially fully specified
    
  append percept to end of percepts
  action  < - -  LOOKUP(percepts,table)
  return action
 

برای هر درک جدید فراخوانی می شود و هر بار فعالیتی را بر می گرداند. با استفاده از برنامه TABLE-DRIVEN-AGENT ساختمان داده های خود، دنباله ادراک را ردیابی می کند.
برنامه TABLE-DRIVEN-AGENT  تابع عامل مطلوب را پیاده سازی میکند. چالش مهم AI، چگونگی نوشتن برنامه ای است که با استفاده از یک کد کوچک (به جای جدول بزرگ)، رفتار عقلایی را انجام دهد. مثال های زیادی داریم که نشان می دهد، این کار امکان پذیر است. به عنوان مثال، جدول های بزرگ ریشه دوم که قبل از دهه 1970 توسط مهندسین و دانش آموزان مورد استفاده قرار گرفت، جای خود را به یک برنامه  5 خطی داده است که از روش نیوتن استفاده میکند و در ماشین حساب های الکترونیکی قابل استفاده است. AI همان کاری را انجام می دهد که نیوتن برای ریشه دوم انجام میدهد.

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

♦ عامل های واکنشی ساده (simple reflex agents)

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

انسان نیز  چنین اتصالاتی دارد، که بعضی از آنها پاسخ های آموخته شده هستند (مثل رانندگی) و بعضی دیگر غریزی هستند (مثل بستن چشم هنگام نزدیک شدن شی ء ای به آن). روش کلی و قابل انعطاف این است که یک مفسر همه منظوره برای قوانین شرط فعالیت ساخته شود و سپس مجموعه ای از قوانین برای محیط های کار خاص ایجاد گردد. در شکل زیر، برنامه ی عامل نشان داده شده است که خیلی ساده است. تابع INTERPRET-INPUT با استفاده از ادراک، یک توصیف انتزاعی از حالت فعلی ایجاد میکند، و تابع RULE-MATCH اولین قانون موجود در مجموعه ای از قوانین را بر می گرداند که با توصیف حالت خاص مطابقت دارد:

function  SIMPLE-REFLEX-AGENT (percept) returns an action
  presistent: ruless, a set of condition-action rules
    
  state  <  - - INTEERPRET-INPUT (percept)
  rule  < - -  RULE-MATCH (state,rules)
  action  <  - -  rule.ACTION
  return action
 

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

اجتناب از حلقه های بی نهایت، در صورتی ممکن است که عامل بتواند فعالیت خود را تصادفی کند. در بعضی موارد، “عامل واکنشی ساده ی تصادفی” ممکن است مثل “عامل واکنشی ساده ی قطعی” عمل کند. رفتار تصادفی درست، در بعضی از محیط های چند عاملی میتواند عقلایی باشد. در محیط های تک عاملی، فعالیت تصادفی معمولا عقلایی نیست. این روش، در بعضی از وضعیت ها به عامل واکنشی ساده کمک می کند. اما در اغلب موارد، با استفاده از عامل های قطعی تخصصی، بهتر می توان عمل کرد.

♦ عامل های واکنشی مبتنی بر مدل (model-based reflex  agents)

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

تغییر این اطلاعات داخلی با مرور زمان، مستلزم دو نوع دانش است که باید در برنامه عامل کدنویسی شود. اولا باید بدانیم که دنیا چگونه مستقل از عامل تکامل می یابد. ثانیا، باید بدانیم که فعالیت های عامل، چه تاثیری در دنیا دارد. این دانش درباره ی “چگونگی عملکرد جهان” چه به صورت مدارهای منطقی ساده پیاده سازی شود یا به صورت تئوری های علمی، مدلی از دنیا نام دارد. عاملی که از چنین مدلی استفاده میکند، عامل مبتنی بر مدل نام دارد. برنامه عامل در شکل زیر نشان داده شده است. بخش جالب، تابع UPDATE-STATE است که مسئول ایجاد توصیف جدیدی از حالت داخلی است. جزئیات چگونگی نمایش مدل ها و حالت ها، به نوع محیط و فناوری استفاده شده در طراحی عامل بستگی دارد.

function  MODEL-BASED-REFLEX-AGENT (percept) returns an action
  presistent: state, the agent's current conception of the world state
              model, a description of how the next state depends on current state and action
              rules, a set of condition-action rules 
              action, the most recent action, initially none
  
  state  < -- UPDATE-STATE (state, action, peercept, model)
  rule  < -- RULE-MATCH (state,rules)
  action  < -- rule.ACTION
  return action
 

عامل واکنشی مبتنی بر مدل، حالت فعلی دنیا را با یک مدل داخلی ردیابی، و همانند عامل واکنشی ساده، فعالیتی را انتخاب میکند.

عامل های هوشمند قسمت 1
عامل های هوشمند قسمت 2
عامل های هوشمند قسمت 3
عامل های هوشمند قسمت 4
عامل های هوشمند قسمت 5

الگوریتم Canny

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

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

 

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

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

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

 

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

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

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

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

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

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

 

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

 


 

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

الگوریتم در 5 مرحله جداگانه اجرا می شود:

1. صاف کردن: تار شدن تصویر برای حذف نویز. پیکربندی توسط فیلتر گاوسی با اندازه مشخص هسته (N) و پارامتر پوشش گاوسی سیگما. پوشاننده فیلتر گاوسی توسط تابع زیر تولید می شود:

private void GenerateGaussianKernel(int N, float S ,out int Weight)
{

float Sigma = S ;
float pi;
pi = (float)Math.PI;
int i, j;
int SizeofKernel=N;

float [,] Kernel = new float [N,N];
GaussianKernel = new int [N,N];
float[,] OP = new float[N, N];
float D1,D2;

D1= 1/(2*pi*Sigma*Sigma);
D2= 2*Sigma*Sigma;

float min=1000;

for (i = -SizeofKernel / 2; i <= SizeofKernel / 2; i++)
{
for (j = -SizeofKernel / 2; j <= SizeofKernel / 2; j++)
{
Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] = ((1 / D1) * (float)Math.Exp(-(i * i + j * j) / D2));
if (Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] < min)
min = Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j];

}
}
int mult = (int)(1 / min);
int sum = 0;
if ((min > 0) && (min < 1))
{

for (i = -SizeofKernel / 2; i <= SizeofKernel / 2; i++)
{
for (j = -SizeofKernel / 2; j <= SizeofKernel / 2; j++)
{
Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] = (float)Math.Round(Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] * mult, 0);
GaussianKernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] = (int)Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j];
sum = sum + GaussianKernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j];
}

}

}
else
{
sum = 0;
for (i = -SizeofKernel / 2; i <= SizeofKernel / 2; i++)
{
for (j = -SizeofKernel / 2; j <= SizeofKernel / 2; j++)
{
Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] = (float)Math.Round(Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] , 0);
GaussianKernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j] = (int)Kernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j];
sum = sum + GaussianKernel[SizeofKernel / 2 + i, SizeofKernel / 2 + j];
}

}

}
//Normalizing kernel Weight
Weight= sum;

return;
}

 

زیر روال ذیل نویز را توسط فیلتر گوسی حذف می کند.

 

private int[,] GaussianFilter(int[,] Data)
        {
            GenerateGaussianKernel(KernelSize, Sigma,out KernelWeight);

            int[,] Output = new int[Width, Height];
            int i, j,k,l;
            int Limit = KernelSize /2;

            float Sum=0;

 Output = Data; // Removes Unwanted Data Omission due to kernel bias while convolution

            for (i = Limit; i <= ((Width - 1) - Limit); i++)
            {
                for (j = Limit; j <= ((Height - 1) - Limit); j++)
                {
                    Sum = 0;
                    for (k = -Limit; k <= Limit; k++)
                    {

                       for (l = -Limit; l <= Limit; l++)
                        {
                            Sum = Sum + ((float)Data[i + k, j + l] * GaussianKernel [Limit + k, Limit + l]);                        

                        }
                    }
                    Output[i, j] = (int)(Math.Round(Sum/ (float)KernelWeight));
                }

            }

            return Output;
        }

 

2. پیدا کردن شیب ها: لبه ها باید مشخص شوند، جایی که شیب های تصویر بزرگ می شوند.

ماسک های سوبل  X و Y برای تولید گرادیان های تصویر X و Y استفاده می شود؛ تابع بعدی تمایز را با استفاده از فیلتر ماسک sobel اعمال می کند.

 

private float[,] Differentiate(int[,] Data, int[,] Filter)
        {
            int i, j,k,l, Fh, Fw;

            Fw = Filter.GetLength(0);
            Fh = Filter.GetLength(1);
            float sum = 0;
            float[,] Output = new float[Width, Height];

            for (i = Fw / 2; i <= (Width - Fw / 2) - 1; i++)
            {
                for (j = Fh / 2; j <= (Height  - Fh / 2) - 1; j++)
                {
                  sum=0;
                   for(k=-Fw/2; k<=Fw/2; k++)
                   {
                       for(l=-Fh/2; l<=Fh/2; l++)
                       {
                          sum=sum + Data[i+k,j+l]*Filter[Fw/2+k,Fh/2+l];


                       }
                   }
                    Output[i,j]=sum;

                }

            }
            return Output;

        }

 

3. توقیف غیر حداکثر: فقط حداکثرهای محلی باید به عنوان لبه ها مشخص شود.

ما جهت گرادیان را پیدا می کنیم و با استفاده از این جهت، ما توقیف غیر حداکثر را انجام می دهیم (“پردازش تصویر دیجیتال- آموزش توسط گنزالس-پیرسون ” را بخوانید)

 

// Perform Non maximum suppression:
           // NonMax = Gradient;

            for (i = 0; i <= (Width - 1); i++)
            {
                for (j = 0; j <= (Height - 1); j++)
                {
                    NonMax[i, j] = Gradient[i, j];
                }
            }
     
            int Limit = KernelSize / 2;
            int r, c;
            float Tangent;

                for (i = Limit; i <= (Width - Limit) - 1; i++)
            {
                for (j = Limit; j <= (Height - Limit) - 1; j++)
                {

                    if (DerivativeX[i, j] == 0)
                        Tangent = 90F;
                    else
                        Tangent = (float)(Math.Atan(DerivativeY[i, j] / DerivativeX[i, j]) * 180 / Math.PI); //rad to degree



                    //Horizontal Edge
                    if (((-22.5 < Tangent) && (Tangent <= 22.5)) || ((157.5 < Tangent) && (Tangent <= -157.5)))
                    {
                        if ((Gradient[i, j] < Gradient[i, j + 1]) || (Gradient[i, j] < Gradient[i, j - 1]))
                            NonMax[i, j] = 0;
                    }

                    //Vertical Edge
                    if (((-112.5 < Tangent) && (Tangent <= -67.5)) || ((67.5 < Tangent) && (Tangent <= 112.5)))
                    {
                        if ((Gradient[i, j] < Gradient[i + 1, j]) || (Gradient[i, j] < Gradient[i - 1, j]))
                            NonMax[i, j] = 0;
                    }

                    //+45 Degree Edge
                    if (((-67.5 < Tangent) && (Tangent <= -22.5)) || ((112.5 < Tangent) && (Tangent <= 157.5)))
                    {
                        if ((Gradient[i, j] < Gradient[i + 1, j - 1]) || (Gradient[i, j] < Gradient[i - 1, j + 1]))
                            NonMax[i, j] = 0;
                    }

                    //-45 Degree Edge
                    if (((-157.5 < Tangent) && (Tangent <= -112.5)) || ((67.5 < Tangent) && (Tangent <= 22.5)))
                    {
                        if ((Gradient[i, j] < Gradient[i + 1, j + 1]) || (Gradient[i, j] < Gradient[i - 1, j - 1]))
                            NonMax[i, j] = 0;
                    }

                }

            }

 

4. آستانه دوگانه: لبه های بالقوه توسط آستانه تعیین می شود.

5. ردیابی لبه توسط هیسترسیس: لبه های نهایی توسط توقیف تمام لبه هایی که به یک لبه بسیار قطعی (قوی) متصل نیستند، مشخص می شوند.

 

private void HysterisisThresholding(int[,] Edges)
        {

            int i, j;
            int Limit= KernelSize/2;


            for (i = Limit; i <= (Width - 1) - Limit; i++)
                for (j = Limit; j <= (Height - 1) - Limit; j++)
                {
                    if (Edges[i, j] == 1)
                    {
                        EdgeMap[i, j] = 1;

                    }

                }

            for (i = Limit; i <= (Width - 1) - Limit; i++)
            {
                for (j = Limit; j <= (Height  - 1) - Limit; j++)
                {
                    if (Edges[i, j] == 1)
                    {
                        EdgeMap[i, j] = 1;
                        Travers(i, j);
                        VisitedMap[i, j] = 1;
                    }
                }
            }




            return;
        }

//Recursive Procedure 
private void Travers(int X, int Y)
        {

            
            if (VisitedMap[X, Y] == 1)
            {
                return;
            }

            //1
            if (EdgePoints[X + 1, Y] == 2)
            {
                EdgeMap[X + 1, Y] = 1;
                VisitedMap[X + 1, Y] = 1;
                Travers(X + 1, Y);
                return;
            }
            //2
            if (EdgePoints[X + 1, Y - 1] == 2)
            {
                EdgeMap[X + 1, Y - 1] = 1;
                VisitedMap[X + 1, Y - 1] = 1;
                Travers(X + 1, Y - 1);
                return;
            }

           //3

            if (EdgePoints[X, Y - 1] == 2)
            {
                EdgeMap[X , Y - 1] = 1;
                VisitedMap[X , Y - 1] = 1;
                Travers(X , Y - 1);
                return;
            }

           //4

            if (EdgePoints[X - 1, Y - 1] == 2)
            {
                EdgeMap[X - 1, Y - 1] = 1;
                VisitedMap[X - 1, Y - 1] = 1;
                Travers(X - 1, Y - 1);
                return;
            }
            //5
            if (EdgePoints[X - 1, Y] == 2)
            {
                EdgeMap[X - 1, Y ] = 1;
                VisitedMap[X - 1, Y ] = 1;
                Travers(X - 1, Y );
                return;
            }
            //6
            if (EdgePoints[X - 1, Y + 1] == 2)
            {
                EdgeMap[X - 1, Y + 1] = 1;
                VisitedMap[X - 1, Y + 1] = 1;
                Travers(X - 1, Y + 1);
                return;
            }
            //7
            if (EdgePoints[X, Y + 1] == 2)
            {
                EdgeMap[X , Y + 1] = 1;
                VisitedMap[X, Y + 1] = 1;
                Travers(X , Y + 1);
                return;
            }
            //8

            if (EdgePoints[X + 1, Y + 1] == 2)
            {
                EdgeMap[X + 1, Y + 1] = 1;
                VisitedMap[X + 1, Y + 1] = 1;
                Travers(X + 1, Y + 1);
                return;
            }


            //VisitedMap[X, Y] = 1;
            return;

        } 
          
        //Canny Class Ends

    }

 

این کار با یک تابع بازگشتی انجام می شود که آستانه دوگانه را با دو آستانه بالا (Threshold (TH و (Low Threshold (TL و تجزیه و تحلیل 8-اتصال انجام می دهد.

 

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

Canny Edge Detection C#

رمز فایل : behsanandish.com


ویژگی‌های جدید در سی شارپ  ۳٫۰

این ورژن از سی شارپ در تاریخ ۱۹ نوامبر سال ۲۰۰۷ به عنوان بخشی از چارچوب دات‌نت ۳٫۵ عرضه شد؛ که شامل ویژگی‌های جدید الهام شده از زبان‌های برنامه‌نویسی اصلی (Functional) مانند Haskell و ML، و الگوی LINQ برای CLR است. در حال حاضر توسط هیچ موسسه استانداردسازی تأیید نشده‌است.

معرفی لینک

لینک (به انگلیسی: Language Integrated Query)(مخفف انگلیسی: LINQ) یک زبان پرس و جوی قابل انعطاف و همه منظوره برای بسیاری از انواع منبع داده‌ها است (مثل انتخاب اشیاء شناور، سندهای XML، بانک‌های اطلاعاتی و…) که در ویژگی‌های سی شارپ ۳ جمع شده‌اند. سینتکس زبان به زحمت از SQL گرفته شده‌است، برای مثال:

int[] array = { 1, 5, 2, 10, 7 };

// Select squares of all odd numbers in the array sorted in descending order
IEnumerable&lt;int&gt; query = from x in array
                         where x % 2 == 1
                         orderby x descending
                         select x * x;

مقدار دهی به اشیاء

Customer c = new Customer(); c.Name = "James";

عبارت بالا می‌تواند به صورت زیر نوشته شود:

Customer c = new Customer { Name="James" };

مقدار دهی Collection

MyList list = new MyList();
list.Add(1);
list.Add(2);

عبارت بالا می‌تواند به صورت زیر نوشته شود:

MyList list = new MyList { 1, 2 };

فرض کنید که اجزای MyList و System.Collections.IEnumerable دارای متد عمومی Add هستند.

انواع داده‌ای بی نام

var x = new { FirstName="James", LastName="Frank" };

سی شارپ ۲٫۰ توابع بی نام را معرفی کرد. سی شارپ ۳٫۰ هم انواع بی نام را معرفی می‌کند. با استفاده از این ویژگی برنامه نویسان قادر خواهند بود به صورت Inline انواع دلخواه خود را ایجاد کنند. به نمونه زیر توجه کنید:

static void Main(string[] args)
{
    var anonymousType = new { Name = string.Empty, Age = 0 };
}

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

چرا Anonymous types؟ انواع بی نام بهترین گزینه برای تولید Entity Typeها می‌باشند. همان‌طور که گفته شد Entity Typeها فقط حاوی داده‌ها هستند؛ بنابراین به بهترین نحو می‌توان داده‌های دریافت شده از کاربر را در انواع بی نام بسته‌بندی کرد.

نتیجه نوع متغیر محلی

var x = new Dictionary < string, List < float >> ();

کد بالا با کد زیر قابل تعویض می‌باشد:

Dictionary < string, List < float >> x = new Dictionary < string, List < float >> ();

این ویژگی تنها یک ntactic sugarراحت برای کوتاه‌تر بیان کردن متغیرهای محلی نمی‌باشد، بلکه برای تعریف متغیرهای بی نام لازم نیز است.

عبارات لامبدا

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

listOfFoo.Where(delegate(Foo x) { return x.Size > 10; })
listOfFoo.Where(x = > x.Size > 10);

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

یکی از ویژگی‌هایی که سی شارپ ۲٫۰ ارائه کرد، توانایی تعریف توابع به صورت Inline بود که این ویژگی با عنوان توابع بی نام (anonymous methods) شناخته می‌شود. توابع بی نام در پاره‌ای مواقع بسیار مفیدند. اما نحو(syntax) به‌کارگیری آن‌ها دشوار می‌باشد. عبارات لامبدا ویژگی توابع بی نام را دارند اما با نحو ساده‌تری در سی شارپ ۳٫۰ معرفی شده‌اند. به نمونه زیر توجه کنید:

static void Main(string[] args)
{
   (int x) = > x + 1;// explicitly typed parameter
   (y, z) = > y * z;// implicitly typed parameter
}

تعریف عبارات لامبدا از نحو (syntax) خاصی پیرو می‌کند. همان‌طور که در کد بالا مشاهده می‌کنید، پارامترهای تابع هم به صورت صریح و هم به صورت ضمنی قابل بیان‌اند. کلمه return به صورت ضمنی حذف شده‌است. تابع معادل عبارت لامبدای اول به صورت زیر است:

int Fn(int x)
{
    return x+1;
}

لیست پارامترها و بدنه عبارت لامبدا توسط => از هم جدا می‌شوند. در صورتی که تعریف عبارت لامبدا بیشتر از یک خط کد باشد می‌توان بدنه آن را با استفاده از {} نشان داد.

static void Main(string[] args)
{
    (int x) = > { x + 1; return x * x; };
}

خواص خودکار

کامپایلر به‌طور خودکار یک متغیر نمونه خصوصی و قرار دهنده و قرار گیرنده مناسب تولید می‌کند، مانند:

public string Name { get; private set; }

توابع بسط داده شده

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

public static class StringExtensions
{
    public static string Left(this string s, int n)
    {
        return s.Substring(0, n);
    }
}

string s = "foo";
s.Left(3);// same as StringExtensions.Left(s, 3);

زبان سی شارپ کلمه کلیدی sealed را برای این منظور ارائه کرد که امکان ارث بری از یک کلاس را صلب کند. یعنی با اضافه شدن این کلمه کلیدی به ابتدای تعریف کلاس، امکان ارث بری از آن غیرممکن می‌شود. سی شارپ ۳٫۰ ویژگی جدیدی را در اختیار برنامه نویسان قرار می‌دهد به این صورت که می‌توان هر نوع کلاسی حتی کلاس‌های مهر شده با Sealed را با استفاده از Extension methodsبسط داد.

توابع جزئی

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

آرایه‌های نوع ضمنی

آرایه‌ها را نیز می‌توان با استفاده از کلمه کلیدی var تعریف کرد.

static void Main(string[] args)
{
    var a = new[] { 1, 10, 100, 1000 };// int[]
    var b = new[] { 1, "one", 2 };// Error
}

پیش پردازنده

ویژگی «دستورها پیش پردازنده» سی شارپ (اگرچه آن‌ها به واقع یک پیش پردازنده نیستند) مبنی بر دستورها پیش پردازنده C است که به برنامه‌نویس اجازه تعریف سمبلهایی را می‌دهند. برخی از این دستورها عبارتند از: #if، #region، #define. راهنماهایی نظیر #region تذکراتی به ویرایش‌گرها برای code folding می‌دهند.

توضیحات کد

توضیحات تک خط با استفاده از دو اسلش تعریف می‌شوند(//) و توضیحات چند خطی با /* شروع و به */ تمام می‌شوند.

public class Foo
{
// a comment
    public static void Bar(int firstParam) {}//Also a comment
}

public class FooBar
{
    /* a comment */
    public static void BarFoo(int firstParam) {}  /* Also a comment */

توضیحات چند خطی هم چنین می‌توانند با /* شروع و با */ تمام شوند.

public class Foo
{
    /* A Multi-Line
       comment  */
    public static void Bar(int firstParam) {}
}

سامانه مستندسازی XML

سامانه مستندسازی #C بسیار شبیه به جاوا است، اما مبنی بر XML. دو شیوه مستندسازی در حال حاضر به وسیله کامپایلر #C پشتیبانی می‌شود.

توضیحات تک خطی، که معمولاً در تولیدکننده کد Visual Studioپیدا می‌شوند، با استفاده از/// شروع می‌شوند.

public class Foo
{
/// < summary > A summary of the method. < /summary >
/// < param name="firstParam" > A description of the parameter. < /param >
/// < remarks > Remarks about the method. < /remarks >
    public static void Bar(int firstParam) {}
}

توضیحات چند خطی، که در نسخه ۱٫۰ تعریف شدند، اما در نسخه ۱٫۱ پشتیبانی از آن‌ها وجود نداشت با /* شروع و به */ ختم می‌شوند:

public class Foo
{
    /** < summary > A summary of the method. < /summary >
     *  < param name="firstParam" > A description of the parameter. < /param >
     *  < remarks>Remarks about the method. < /remarks > */
    public static void Bar(int firstParam) {}
}

نکته:در اینجا یک ملاک سخت در مورد استفاده از فضاهای خالی در سندهای XML هنگام استفاده از /**وجود دارد:

/**
 * < summary >
 * A summary of the method. < /summary > */

نوع دیگری از کد بالا ارائه خواهد شد:

/**
 * < summary >
   A summary of the method. < /summary > */

سینتکس سندسازی توضیحات XML در یک ضمیمه بی قاعده از استاندارد ECMA از سی شارپ وجود دارد. یک استاندارد مشابه قوانینی برای پردازش توضیحات و تبدیل آن‌ها به متون Plain در XML را با کمک قوانین CLI فراهم می‌کند. این به هر IDE در سی شارپ و دیگر ابزار گسترش دهنده امکان پیدا کردن هر نمادی را در کدها می‌دهد.

(CLR(Common Language Runtime

بخش مرکزی چارچوب دات‌نت، محیط اجرایی Runtime می‌باشد که اصطلاحاً به آن CLR یا .NET Runtime می‌گویند. کدهایی که تحت کنترل CLR اجرا می‌شوند اغلب به عنوان کدهای مدیریت شده نامیده می‌شوند.

اگر چه، پیش از این که کدها (همه زبان‌های چارچوب دات‌نت) به وسیله CLR اجرا شوند، بایستی مورد کامپایل قرار گیرند. در چارچوب دات‌نت عمل کامپایل در دو مرحله صورت می‌گیرد:

  1. کامپایل سورس کد به MSIL.
  2. کامپایل MSIL به کد مختص پلتفرم به وسیله CLR

یک نکته قابل توجه، اشتراک زبان میانی مایکروسافت با کد بایت جاوا(Bytecode)است. ایده این اشتراک از آنجا سرچشمه گرفت که چون Bytecode یک زیان سطح پایین با یک دستور زبان ساده می‌باشد (که به جای متن مبتنی بر کدهای عددی است)، می‌تواند به سرعت به کدهای بومی(Native) ترجمه شود.

برخی ویژگی‌های MSIL

  • شیءگرایی و بکارگیری واسط‌ها
  • تمایز فراوان بین انواع مقداری و ارجاعی
  • تعیین Strong Type (این نوع داده دیگر معتبر نیست)
  • مدیریت خطا از طریق به‌کارگیری Exception
  • بکارگیری صفات

 

منبع

 

آشنایی با #C قسمت 1
آشنایی با #C قسمت 2
آشنایی با #C قسمت 3

منبع


منابع:

fa.wikipedia.org

http://mediasoft.ir

 

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

سیستم توصیه گر (به انگلیسی: Recommender System) یا سامانه پیشنهادگر (واژه سیستم یا سامانه گاهی با پلتفرم یا موتور جایگزین می‌شود)، با تحلیل رفتار کاربر خود، اقدام به پیشنهاد مناسب‌ترین اقلام (داده، اطلاعات، کالا و…)می‌نماید. این سیستم رویکردی است که برای مواجهه با مشکلات ناشی از حجم فراوان و رو به رشد اطلاعات ارائه شده‌است و به کاربر خود کمک می‌کند تا در میان حجم عظیم اطلاعات سریع‌تر به هدف خود نزدیک شوند. برخی سامانه پیشنهادگر را معادل پالایش گروهی (به انگلیسی: Collaborative filtering) می‌دانند.

مقدمه

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

تعاریف متفاوتی برای سیستم‌های توصیه‌گر ارائه شده‌است. از آن جمله، تعریف کلی‌نگر و خلاصه آقای Ting-peng liang در سال ۲۰۰۷ است که RS را زیرمجموعه‌ای از DSSها می‌داند و آن‌ها راسیستم‌های اطلاعاتی تعریف می‌کند که، توانایی تحلیل رفتارهای گذشته و ارائه توصیه‌هایی برای مسائل جاری را دارا هستند. به زبان ساده‌تر در سیستم‌های توصیه‌گر تلاش بر این است تا با حدس زدن شیوه تفکر کاربر (به کمک اطلاعاتی که از نحوه رفتار وی یا کاربران مشابه وی و نظرات آن‌ها داریم) به وی مناسب‌ترین و نزدیک‌ترین کالا به سلیقه او را شناسایی و پیشنهاد کنیم. این سیستم‌ها در حقیقت همان فرایندی که ما در زندگی روزمره خود به کار می‌بریم و طی آن تلاش می‌کنیم تا افرادی با سلایق نزدیک به خود را پیدا کرده و از آنها در مورد انتخاب‌هایمان نظر بخواهیم. توصیه‌هایی که از سوی سیستم‌های توصیه‌گر ارائه می‌شوند به طور کلی می‌توانند دو نتیجه دربرداشته باشند:

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

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

مزایا و پیشرفت‌ها

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

تاریخچه

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

ظرفیت رایانه‌ها در فراهم آوردن توصیه‌ها تقریباً از همان اوایل تاریخ‌چه رایانه‌ها شناخته شد. گراندی، یک کتابدار کامپیوتری گامی اولیه به سمت سامانه‌های توصیه‌گر خودکار بود. این کتابدار یک توصیه‌گر نسبتاً ساده و اولیه بود که کاربران را به قالب‌هایی بر اساس مصاحبه کوتاه با استفاده از اطلاعات مستقیم‌کدشده(hard-coded) دربارهٔ سلایق کتاب قالب‌های مختلف گروه‌بندی می‌کرد تا توصیه‌ها را تولید کند، ولی این کار ورود اولیه مهم به فضای سامانه‌های توصیه‌گر قلمداد می‌شود.

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

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

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

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

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

جعبه ابزار تکنیک‌های توصیه گر به چیزی بیش از پالایش مشارکتی گسترش یافته‌اند و شامل رویکردهای محتوامحور(Content-Based) بر اساس متدهای بازیابی اطلاعات، استنتاج بیزی (Bayesian Inference) و استدلال مورد محور (Case-Based Reasonong) می‌باشد. این متدها بجای یا درعوض الگوهای نمره دهی کاربران، محتوا یا ویژگی‌های اصلی آیتم‌هایی که قرار است توصیه شود را درنظر می‌گیرند. با به بلوغ رسیدن استراتژی‌های توصیه مختلف، سامانه‌های توصیه‌گر ترکیبی (Hybrid Recommender Systems) نیز ظهور یافته‌اند و الگوریتم‌های مختلفی را در سیستم‌های مرکبی ترکیب کرده‌اند که بر اساس قدرت الگوریتم‌های تشکیل‌دهنده‌شان ایجاد شده‌اند. البته در کنار رویکردهای محتوا محور، پالایش مشارکتی، هم روش تکی و هم ترکیب‌شده‌اش به عنوان روشی مؤثر همچنان مطرح هستند.

زمانی که Netflix جایزه Netflix Prize را در سال ۲۰۰۶ به منظور بهبود بخشیدن وضعیت توصیه‌های فیلمش برقرار کرد، تحقیق بر روی الگوریتم‌های سامانه‌های توصیه‌گر توجه بسیاری را به خودش جلب کرد. هدف این رقابت ساختن یک الگوریتم توصیه‌گری بود که بتواند الگوریتم CineMatch که متعلق به خود Netflix بود را با ۱۰٪ بهبود در آزمایشات آفلاین شکست دهد. این امر موجب ایجاد خروشی از اقدامات شد، هم در بین محیط آکادمیک و هم در بین سایر علاقمندان. جایزه یک میلیون دلاری ارزشی را که فروشندگان برای دقت توصیه‌ها قائل هستند نشان می‌دهد[۱].

کاربردها

سیستم‌های توصیه‌گر کاربردهای فراوانی دارند که برخی از زمینه‌های کاربردی آن به شرح زیر است:

  • تجارت الکترونیک: برای توصیه محصولات و خدمات مختلف.
  • اینترانت‌های بنگاهی: برای پیدا کردن افراد خبره در یک زمینه خاص یا افرادی که در رویارویی با شرایط مشابه، تجاربی کسب کرده و راه حل‌هایی یافته‌اند (بیشتر داخل یک سازمان کاربرد دارد).
  • کتابخانه دیجیتال: پیدا کردن کتاب، مقاله و …
  • کاربردهای پزشکی: انتخاب پزشک متناسب با شرایط (مکان، نوع بیماری، زمان و …) بیمار، انتخاب دارو و …
  • مدیریت ارتباط با مشتری CRM: برای ارائه راهکارهایی برای حل مشکلات تولیدکننده و مصرف‌کننده در زنجیره تأمین.

مقایسه سامانه‌های توصیه گر و سامانه‌های تصمیم‌یار کلاسیک

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

تعاریف و اصطلاحات عمده

لازم است برای درک مفهوم سیستم توصیه‌گر، مفاهیم چهارگانه و ابتدایی زیر را بررسی کنیم.

  • در سیستم‌های توصیه گر به کاربری که توصیه جاری در سیستم، برای وی در حال پردازش و آماده شدن است، کاربر فعال یا کاربر هدف می‌گویند.
  • الگوریتم‌های به کار رفته در این سیستم‌ها، از ماتریسی به نام ماتریس رتبه‌ها استفاده می‌کنند؛ اصطلاحات رایج برای این ماتریس Rating Database و Preference Database نیز هستند.
  • از فعل مصرف کردن در سیستم‌های توصیه‌گر، زمانی استفاده می‌کنند که کاربر توصیه ارائه شده را می‌پذیرد. به عبارتی وقتی کاربری پیشنهادی را که توسط سیستم به وی شده می‌پذیرد، می‌گوییم کاربر آن پیشنهاد را مصرف کرده، این پذیرش می‌تواند به شکل‌های مختلفی باشد، مثلاً کاربر، کتاب پیشنهادی را می‌خرد، سایت پیشنهادی را مرور می‌کند یا به شرکت خدماتی ای که به او پیشنهاد شده مراجعه می‌کند. ساختار ماتریس رتبه‌ها بدین گونه‌است که در آن، هر سطر ماتریس نمایانگر یک کاربر و هر ستون آن معرف کالایی (شئای) خاص است.

حال با مفهوم تابع سودمندی آشنا خواهیم شد که قصد داریم به کمک آن یک مدل کلی ریاضی از سیستم‌های توصیه‌گر را نیز ارائه دهیم. در واقع یک سیستم توصیه‌گر را می‌توان با این نگاشت همسان دانست و مدل کرد: {\displaystyle u:C*S->R}{\displaystyle u:C*S->R}

فرض کنید C مجموعه تمامی کاربران و S مجموعه اقلام در دسترس باشند. تابعی را که میزان مفید و متناسب بودن کالای S برای کاربر C را محاسبه می‌کند با u نشان می‌دهیم، که در آن R مجموعه‌ای است کاملاً مرتب (براساس میزان اهمیت). هرکدام از عناصر S را می‌توان با مجموعه‌ای از خصوصیات، مشخص کرد. برای مثال، محصولی مثل فیلم را می‌توان با مشخصه‌هایی چون عنوان فیلم، کارگردان، طول زمانی فیلم، تاریخ تولید و … ثبت کرد. همچنین عناصر مجموعه C را نیز می‌توان بر اساس ویژگی‌های مثل سن، جنسیت و … ثبت کرد. (باید توجه داشت که u روی تمام فضای مجموعه آغازین S×C تعریف شده نیست؛ از این رو باید برون‌یابی شود)

سیستم توصیه گر (Recommender System) قسمت 1
سیستم توصیه گر (Recommender System) قسمت 2
سیستم توصیه گر (Recommender System) قسمت 3