نوشته‌ها

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

مقدمه

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

شرح خبر

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

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

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

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

 

مرحله ۴: سرکوب لبه های غیر حداکثر

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

 

</pre>
<pre>#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 in C++ -No2

رمز فایل : behsanandish.com

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

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

تا این لحظه از مجموعه مطالب مرتبط با مباحث Asynchronous Programming در سی شارپ با ماهیت Asynchronous در delegate ها، کار با Thread ها و کتابخانه TPL در دات نت آشنا شدیم. اما باز هم در برخی سناریو ها و انجام کارهای پیچیده در برنامه نویسی Asynchronous، نیاز به حجم زیادی از کدها دارد.

از نسخه ۴٫۵ دات، در زبان سی شارپ (و همینطور زبان VB) دو کلمه کلیدی اضافه شد که اجازه نوشتن کدهای Asynchronous را به شکل دیگری به برنامه نویسان می داد. این دو کلمه کلیدی، کلمات async و await هستند و زمانی که شما در کدهای خود از این دو کلمه کلیدی استفاده می کنید، در زمان کامپایل کدها، کامپایلر کدهایی را برای شما تولید می کند که به صورت بهینه و البته مطمئن کارهای Asynchronous را برای شما انجام می دهند، کدهای تولید شده از کلاس هایی که در فضای نام System.Threading.Tasks قرار دارند استفاده می کنند.

نگاه اولیه با ساختار async و await

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

برای آشنایی بیشتر برنامه ای از نوع Windows Forms Application ایجاد کرده، یک Button بر روی فرم قرار می دهیم. زمانی که بر روی Button ایجاد شده کلیک می شود، یک متد دیگر فراخوانی شده و بعد از یک وقفه ۱۰ ثانیه ای عبارتی را بر میگرداند و در نهایت این متن به عنوان Title برای فرم برنامه ست می شود:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }
 
    private void CallButton_Click(object sender, EventArgs e)
    {
        this.Text = DoWork();
    }
 
    private string DoWork()
    {
        Thread.Sleep(10000);
        return "Done.";
    }
}

مشکلی که وجود دارد این است که بعد از کلیک بر روی Button ایجاد شده، ۱۰ ثانیه باید منتظر شده تا عنوان فرم تغییر کند. اما با انجام یکسری تغییرات در کد بالا، می توان بوسیله کلمات کلیدی async و await کاری کرد که عملیات اجرای متد به صورت Asynchronous انجام شود. برای اینکار کد بالا را به صورت زیر تغییر می دهیم:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }
 
    private async void CallButton_Click(object sender, EventArgs e)
    {
        this.Text = await DoWork();
    }
 
    private Task<string> DoWork()
    {
        return Task.Run(() = >
        {
            Thread.Sleep(10000);
            return "Done.";
        });
    }
}

بعد از اجرای برنامه، خواهیم دید که فرم ما به قول معروف block نمی شود، یعنی تا زمان اتمام فراخوانی DoWork می توانیم کارهای دیگری در فرم انجام دهیم. اگر در کد بالا دقت کنید، متدی که برای رویداد Click دکمه CallButton تعریف شده، با کلمه کلیدی async مشخص شده، یعنی اجرای این متد باید به صورت Aynchronous انجام شود.

علاوه بر این، داخل بدنه این متد، زمان فراخوانی DoWork از کلمه await استفاده کردیم، دقت کنید که نوشتن کلمه کلیدی await اینجا الزامی است، اگر این کلمه کلیدی نوشته نشود، زمان اجرای DoWork باز هم عملیات فراخوانی متد باعث block شدن فرم ما می شود. همچنین دقت کنید که متد DoWork به جای اینکه مقدار string برگرداند، مقداری از نوع <Task<string بر میگرداند. به طور خلاصه کاری که DoWork انجام می دهد به صورت زیر است:

زمانی که متد DoWork فراخوانی می شود، یک Task جدید اجرا می شود و داخل Task ابتدا عملیات اجرای Thread به مدت ۱۰ ثانیه متوقف می شود و بعد از ۱۰ ثانیه یک رشته به عنوان خروجی برگردانده می شود. البته این رشته تحت یک شئ از نوع Task به متدی که DoWork را فراخوانی کرده بازگردانده می شود.

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

قواعد نام گذاری برای متدهای Async

همانطور که گفتیم، داخل متدهایی که با async مشخص شده اند، حتماً می بایست کلمه کلیدی await نیز نوشته شود. اما از کجا بدانیم کدام متدها می توانند به صورت Async فراخوانی شوند؟ یعنی نوع خروجی آن ها یک Task است؟ اصطلاحاً به متدهایی که خروجی آن ها از نوع <Task<T است Awaitable گفته می شود. برای اینکار باید از قواعد نامگذاری متدهای Async پیروی کنیم. بر اساس مستندات مایکروسافت، می بایست کلیه متدهایی که مقدار خروجی آن ها از نوع Task است، به صورت async تعریف شوند و در انتهای نام متد کلمه Async نوشته شود، بر اساس مطالب گفته شده، متد DoWork را به صورت زیر تغییر می دهیم:

private async Task<string> DoWorkAsync()
{
    return await Task.Run(() = >
    {
        Thread.Sleep(10000);
        return "Done.";
    });
}

با انجام تغییرات بالا، کد رویداد Click را برای CallButton به صورت زیر تغییر می دهیم:

private async void CallButton_Click(object sender, EventArgs e)
{
    this.Text = await DoWorkAsync();
}

متدهای Async با مقدار خروجی void

در صورتی که متدی که قرار است به صورت async فراخوانی شود، مقدار خروجی ندارد می توان نوع خروجی متد را از نوع کلاس غیر جنریک Task انتخاب کرد و کلمه کلیدی return را ننوشت:

private async Task DoWorkAsync()
{
    await Task.Run(() = >
    {
        Thread.Sleep(10000);
    });
}

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

await DoWorkAsync();
MessageBox.Show("Done.");

متدهای async با چندین await

یکی از قابلیت های async و await، نوشتن چندین قسمت await در یک متد async است. نمونه کد زیر حالت گفته شده را نشان می دهد:

private async void CallButton_Click(object sender, EventArgs e)
{
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("First Task Done!");
 
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("Second Task Done!");
 
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("Third Task Done!");
}

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

منبع


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

 

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

پیش از این ما در سری مطالب مرتبط با بحث کار با Thread با نحوه ایجاد و مدیریت Thread ها در دات نت آشنا شدیم. از نسخه ۴ دات نت قابلیتی اضافه شد با نام Task Parallel Programming یا TPL که روش جدیدی برای نوشتن برنامه Multi-Theaded است. این قابلیت بوسیله یکسری از کلاس ها که در فضای نام System.Threading.Tasks قرار دارد فراهم شده و به ما این اجازه را می دهد که بدون درگیر شدن مستقیم با Thread ها و Thread Pool ها برنامه های Multi-Threaded بنوسیم.

دقت کنید که زمان استفاده از قابلیت TPL دیگر نیازی به استفاده از کلاس های فضای نام System.Threading نمی باشد و به صورت پشت زمینه عملیات ساخت و مدیریت Thread ها برای ما انجام می شود. با این کار شیوه کار با Threadها بسیار ساده شده و یکسری از پیچیدگی ها در این بین حذف می شود.

فضای نام System.Threading.Tasks

همانطور که گفتیم TPL در حقیقت مجموعه ای از کلاس ها است که در فضای نام System.Threading.Tasks قرار گرفته. یکی از قابلیت های TPL این است که کارهای محوله را به صورت خودکار بین CPU های سیستم (در صورت وجود) توزیع می کند که این کار در پشت زمینه بوسیله CLR Thread Pool انجام می شود.

کارهای انجام شده توسط TPL در پشت زمینه عبارتند از تقسیم بندی وظایف، زمانبندی Thread ها، مدیریت وضعیت (State Management) و یکسری از کارهای اصطلاحاً Low-Level دیگر. نتیجه این کار برای شما بالا رفتن کارآیی برنامه ها بوده بدون اینکه درگیر پیچیدگی های کار با Thread ها شوید. همانطور که گفتیم فضای نام System.Threading.Tasks شامل یکسری کلاس ها مانند کلاس Parallel، کلاس Task و … می باشد که در ادامه با این کلاس ها بیشتر آشنا می شویم.

نقش کلاس Parallel

یکی از کلاس های TPL که نقش کلیدی را در نوشتن کدهای Parallel ایفا می کند، کلاس Parallel است، این کلاس یکسری متدها در اختیار ما قرار می دهد که بتوانیم بر روی آیتم های یک مجموعه (علی الخصوص مجموعه هایی که اینترفیس IEnumerable را پیاده سازی کرده اند) به صورت parallel عملیات هایی را انجام دهیم.

متدهای این کلاس عبارتند از متد های For و ForEach که البته Overload های متفاوتی برای این متدها وجود دارد. بوسیله این متدها می توان کدهایی نوشتن که عملیات مورد نظر را به صورت parallel بر روی آیتم های یک مجموعه انجام دهند. دقت کنید کدهایی که برای این متدها نوشته می شوند در حقیقت همان کدهایی هستند که معمولاً در حلقه های for و foreach استفاده می شوند، با این تفاوت که به صورت parallel اجرا شده و اجرا و مدیریت کدها بوسیله thread ها و CLR Thread Pool انجام شده و البته بحث همزمانی نیز به صورت خودکار مدیریت می شود.

کار با متد ForEach

در ابتدا به سراغ متد ForEach می رویم، این متد یک مجموعه که ایترفیس IEnumerable را پیاده سازی کرده به عنوان پارامتر اول و متدی که باید بر روی هر یک اعضای این مجموعه انجام شود را به عنوان پارامتر دوم قبول می کند:

var numbers = new List &lt; int &gt; {2, 6, 8, 1, 3, 9, 6, 10, 5, 4};
Parallel.For(3, 6, index  = &gt;
{
    Console.WriteLine(numbers[index]);
    Console.WriteLine("Thread Id: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
});

در کد بالا یک آرایه از لیست از نوع int تعریف کرده و در مرحله بعد بوسیله متد ForEach در کلاس Parallel اعضای لیست را پردازش می کنیم، با هر بار اجرا خروجی های متفاوتی دریافت خواهیم کرد:

۸
۵
Thread Id: 6
۴
۲
Thread Id: 1
۶
۳
Thread Id: 5
۹
Thread Id: 5
۱۰
Thread Id: 5
۱
Thread Id: 5
Thread Id: 4
Thread Id: 6
۶
Thread Id: 1
Thread Id: 3

همانطور که مشاهده می کنید شناسه های مربوط به thread در هر بار اجرای کدی مشخص شده در متد ForEach با یکدگیر متفاوت است، دلیل این موضوع ایجاد و مدیریت Thread ها توسط CLR Thread Pool است که ممکن است با هر بار فراخوانی متد مشخص شده به عنوان پارامتر دوم یک thread جدید ایجاد شده یا عملیات در یک thread موجود انجام شود.

کار با متد For

اما علاوه بر متد ForEach متد For نیست را می توان برای پردازش یک مجموعه استفاده کرد. در ساده ترین حالت این متد یک عدد به عنوان اندیس شروع حلقه، عدد دوم به عنوان اندیس پایان حلقه و یک پارامتر که متدی با پارامتر ورودی از نوع int یا long که نشان دهنده اندیس جاری است قبول می کند، برای مثال در متد زیر بوسیله متد For لیست numbers را در خروجی چاپ می کنیم، اما نه همه خانه های آن را پس عبارت اند از:

var numbers = new List &lt; int &gt;  {2, 6, 8, 1, 3, 9, 6, 10, 5, 4};
Parallel.For(3, 6, index  = &gt;
{
    Console.WriteLine(numbers[index]);
    Console.WriteLine("Thread Id: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
});

با اجرای کد بالا خروجی زیر نمایش داده می شود، البته با هر بار اجرا ممکن است خروجی ها با هم متفاوت باشند:

۱
Thread Id: 1
۹
۳
Thread Id: 3
Thread Id: 4

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

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

منبع



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

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

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

  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

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

سامانه‌های توصیه‌گر به طور کلی به سه دسته تقسیم می‌شوند؛ در رایج‌ترین تقسیم‌بندی، آنها را به سه گروه ۱. محتوا محور ۲. دانش محور و ۳. صافی سازی تجمعی، تقسیم می‌کنند، که البته گونه چهارمی تحت عنوان 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) قسمت ۳

لینکدین چیست؟

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

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

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

ورود به صفحه شخصی شرکت بهسان اندیش در سایت لینکدین

 

صفحه شخیص شرکت بهسان اندیش در لینکدین

 

سیستم خبره

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

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

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

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

پیشگفتار

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

تاریخچه

تا ابتدای دههٔ ۱۹۸۰ (م) کار چندانی در زمینهٔ ساخت و ایجاد سیستم های خِبره توسط پژوهش گران هوش مصنوعی صورت نگرفته بود. از آن زمان به بعد، کارهای زیادی در این راستا و در دو حوزهٔ متفاوت ولی مرتبط سیستم های کوچک خبره و نیز سیستم های بزرگ خبره انجام شده است.

در دهه ۱۹۷۰، ادوارد فیگن بام در دانشگاه استانفورد به دنبال کشف روش حل مسئله ای بود که خیلی کلی و همه منظوره نباشد. پژوهشگران دریافتند که یک متخصص معمولاً دارای شماری رموز و فوت و فن خاص برای کار خود می‌باشد و در واقع از مجموعه‌ای از شگردهای سودمند و قواعد سرانگشتی در کار خود بهره می‌برد، این یافته مقدمه پیدایش سیستم خبره بود. سیستم خبره با برگرفتن این قواعد سر انگشتی از متخصصین و به تعبیری با تبدیل فرایند استدلال و تصمیم‌گیری متخصصین به برنامه‌های رایانه‌ای می‌تواند به عنوان ابزار راهنمای تصمیم‌گیری در اختیار غیرمتخصص و حتی متخصصین کم تجربه قرار گیرد.

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

مغز انسان به بخش‌هایی تقسیم شده است که هر بخش وظیفه خاص خود را جدا از بقیه انجام می‌دهد. آشفتگی در کار یک بخش تأثیری در دیگر بخشهای مغز نخواهد گذاشت. در برنامه‌های هوش مصنوعی نیز این مسئله رعایت می‌شود درحالی که در برنامه‌های غیر هوش مصنوعی مثل C یا Pascal تغییر در برنامه روی سایر قسمت‌های برنامه و اطلاعات تأثیر دارد.

مباحث کاربردی و مهم در تحقق یک سیستم هوش مصنوعی:

حوزه‌های کاربرد

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

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

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

کاربرد سیستم های خبره در خدمات کتابداری و اطلاع‌رسانی

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

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

کاربرد سیستم های خبره در حسابداری و امور مالی

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

کاربردهای مختلفی از سیستم های خبره در سه زمینه حسابداری، حسابداری مدیریت و امور مالیاتی به شرح ذیل می‌باشد:

    • حسابرسی :ارزیابی ریسک – تهیه برنامه حسابرسی – فراهم آوردن کمکهای فنی – کشف تقلبات و جلوگیری از آنها
    • حسابداری مدیریت :قیمت گذاری محصولات و خدمات – تعیین بهای تمام شده – طراحی سیستمهای حسابداری – بودجه بندی سرمایه‌ای – انتخاب روش حسابداری – ارزیابی اعتبار – ایجاد و برقراری واپاشی (کنترل)
    • امور مالیاتی : توصیه‌های مالیاتی – محاسبه مابه التفاوتهای مالیاتی – برنامه‌ریزی مالی شخصی.

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

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

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

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

    1. کمک به کسب بینش یا بینش آفرین Insight facilitaing
    2. آسان سازی تصمیم‌گیری Decision facilitating
    3. تصمیم سازی Decision Making

سیستم های خبره بینش آفرین

در این نوع سیستم ها، هدف اصلی ارائه پردازش‌های مربوط به کمک واکاوی نسبتها و نمودار هاست این نسبت‌ها و نمودارها برای دست اندرکاران تحلیل مالی در ایجاد بینش دقیق تری در مورد وضع مالی و چشم‌انداز آتی یک مؤسسه، یعنی سودمند است با چنین هدفی عملاً مراحل اول و دوم از فرایند چهار مرحله‌ای تصمیم‌گیری به کمک این سیستم ها انجام می‌شود این نرم‌افزارها را به این دلیل بینش آفرین می‌خوانیم که هدفشان کمک به کارگزاران و دست اندرکاران مالی برای انجام یک مشاهده بینش آفرین مشخص است بنابر این درجه از کارآزمودگی و تخصص موجود در زمره سیستم های خبره واقعی به حساب آورده نمونه‌هایی از این قبیل نرم‌افزارها عبارتند از: INsiGht و NEWVIEWS که هر دو عملاً سیستم های جامع حسابداری مشتمل بر تحلیلهای مالی اند یعنی در عین اینکه همه عملیات حسابداری را انجام می‌دهند. در محیطهای شبیه صفحه گسترده تحلیلهای مالی خود را نیز عرضه می‌کنند نرم‌افزار شناخته شده دیگر REFLEX نام دارد که ۱۲ نسبت کلیدی را محاسبه کرده و تحلیل و تفسیرهای پیشنهادی خود را نیز ارائه می‌کند این تحلیلها همراه با ارائه نسبت‌ها، روندها و نمودارهای مناسب است.

سیستم های خبره آسان کننده تصمیم‌گیری

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

سیستم های خبره

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

— متخصص: آیا میوه سبز است؟

— استفاده کننده: خیر.

— متخصص: آیا میوه قرمز است؟

— استفاده کننده: بله.

— متخصص: آیا این میوه روی درخت رشد می‌کند؟

— استفاده کننده: خیر.

— متخصص: آیا این میوه روی یک بوته رشد می‌کند؟

— استفاده کننده: بله.

— متخصص: آیا این بوته تیغ دارد؟

— استفاده کننده: بله.

— متخصص: این میوه تمشک است!

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

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

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

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

تفاوت سیستم های خبره با سایر سیستم های اطلاعاتی

سیستم های خبره برخلاف سیستم های اطلاعاتی که بر روی داده‌ها(Data) عمل می‌کنند، بر دانش (Knowledge) متمرکز شده است. همچنین دریک فرایند نتیجه‌گیری، قادر به استفاده از انواع مختلف داده‌ها عددی(Digital)، نمادی Symbolic و مقایسه‌ای (Analog) می‌باشند. یکی دیگر از مشخصات این سیستم‌ها استفاده از روشهای ابتکاری (Heuristic) به جای روشهای الگوریتمی می‌باشد. این توانایی باعث قرار گرفتن دامنهٔ گسترده‌ای از کاربردها در برد عملیاتی سیستم های خبره می‌شود. فرایند نتیجه‌گیری در سیستم های خبره بر روشهای استقرایی و قیاسی پایه‌گذاری شده است. از طرف دیگر این سیستم ها می‌توانند دلایل خود در رسیدن به یک نتیجه‌گیری خاص یا جهت و مسیر حرکت خود به سوی هدف را شرح دهند. با توجه به توانایی این سیستم‌ها در کار در شرایط فقدان اطلاعات کامل یا درجات مختلف اطمینان در پاسخ به پرسشهای مطرح‌شده، سیستم های خبره نماد مناسبی برای کار در شرایط عدم اطمینان(Uncertainty) یا محیطهای چند وجهی می‌باشند.

سیستم خبره قسمت ۱
سیستم خبره قسمت ۲
سیستم خبره قسمت ۳
سیستم خبره قسمت ۴
سیستم خبره قسمت ۵
سیستم خبره قسمت ۶

تشخیص لبه در تصاویر

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

ویژگی‌های لبه

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

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

آشکارسازی لبه

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

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

 ۵ ۷ ۶ ۴ ۱۵۲ ۱۴۸ ۱۴۹

محاسبه مشتق اول

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

اگر I(x) نماینده شدت روشنایی پیکسل x، و I′(x) نماینده مشتق اول(گرادیان شدت روشنایی) در پیکسل x باشد، بنابراین داریم:

{\displaystyle I'(x)=-1\cdot I(x-1)+0\cdot I(x)+1\cdot I(x+1).\,}

برای پردازش تصویر با عملکرد بهتر، مشتق اول را می‌توان(در یک بعد) با چرخاندن با ماسک زیر روی تصویر بدست آورد:

−۱۰۱

محاسبهٔ مشتق دوم

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

اگر I(x) نمایشگر شدت نور در نقطه x و I′′(x) مشتق دوم در نقطه x باشد:

{\displaystyle I''(x)=1\cdot I(x-1)-2\cdot I(x)+1\cdot I(x+1).\,}

اینجا نیز بیشتر الگوریتم‌ها از یک ماسک پیچش برای پردازش سریع داده‌های تصویر سود می‌برند:

−۲

آستانه‌گیری

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

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

عملگرهای آشکارسازی لبه

  • مرتبه نخست: رابرتز، پرویت، سوبل، کنی، اسپیسک
  • مرتبه دوم: لاپلاسی، مار-هیلدرث

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

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

(استجر، ۱۹۹۸)An Unbiased Detector of Curvilinear Structure