بایگانی برچسب برای: پردازش تصویر

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

زمانی که عملیاتی را به عنوان یک Task اجرا می کنیم، ممکن است بخواهیم آن Task را در حین اجرا متوقف کنیم، برای مثال، Task ای داریم که در حال پردازش 1000 فایل است و کاربر باید این امکان را داشته باشد که Task در حال اجرا را متوقف کند. عملیات متوقف کردن Task ها هم برای متدهای کلاس Parallel امکان پذیر است و هم کلاس Task. برای اینکار می بایست از کلاس CancellationToken استفاده کنیم. برای مثال Task زیر را در نظر بگیرید که حاصل میانگین جمع اعداد 1 تا 100 را محاسبه می کند:

Task < int > averageTask = new Task < int > (() =>
{
    Console.WriteLine("Calculating average...");
    Console.WriteLine("Press Ctrl+C to cancel...");
    var sum = 0;
    for (int counter = 1; counter < = 100; counter++)
    {
        sum += counter;
        Thread.Sleep(100);
    }
    Console.WriteLine("All done.");
    return sum/100;
});
averageTask.Start();
Console.WriteLine(averageTask.Result);

قبلاً با این کد آشنا شدیم، اما کاری که در این قسمت می خواهیم انجام دهیم اضافه کردن قابلیتی است که کاربر بتواند با فشردن کلید های Ctrl+C عملیات را متوقف کند. برای اینکار ابتدا شئ ای از نوع کلاس CancellationTokenSource که در فضای نام System.Threading قرار دارد، در کلاس Program به صورت زیر تعریف می کنیم:

Task < int > averageTask = new Task < int > (() = >
{
    Console.WriteLine("Calculating average...");
    Console.WriteLine("Press q to cancel...");
    var sum = 0;
    for (int counter = 1; counter < = 100; counter++)
    {
        sum += counter;
        Thread.Sleep(100);
    }
    Console.WriteLine("All done.");
    return sum/100;
}, source.Token);

شئ source که در کلاس Program ایجاد کردیم متدی دارد با نام Cancel که این متد را زمانی که قصد داریم Task متوقف شود باید فراخوانی کنیم. فراخوانی این متد باید زمانی انجام شود که کاربر کلید های Ctrl+C را فشار داده است. در محیط Console، زمانی که کاربر کلید های Ctrl+C را فشار می دهد، event ای با نام CancelPressKey در کلاس Console فراخوانی می شود، پس باید این از این event برای فراخوانی متد Cancel به صورت زیر استفاده کنیم:

Console.CancelKeyPress += (sender, eventArgs) = >
{
    source.Cancel();
    eventArgs.Cancel = true;
};

به خط دوم داخل event دقت کنید، زمانی که کلید های Ctrl+C فشرده می شوند، به صورت پیش فرض کل برنامه Console متوقف می شود، برای جلوگیری از این کار مقدار خصوصیت Cancel را در شئ eventArgs به مقدار true ست می کنیم، یعنی عملیات متوقف کردن محیط کنسول به صورت دستی توسط ما انجام شده و خود سیستم نیاز به انجام کاری در این باره ندارد.

بعد از Subscribe کردن event بالا، باید به برنامه بگوییم تا زمانی که task به اتمام نرسیده یا کاربر کلید های Ctrl+C را فشار نداده نباید از برنامه خارج شویم، به همین خاطر یک حلقه while به صورت زیر ایجاد می کنیم:

while (!averageTask.IsCompleted &amp;&amp; !source.IsCancellationRequested)
{                                                                                                
}

با خصوصیت IsCompleted در کلاس Task قبلاً آشنا شدیم، اما خصوصیت IsCancellationRequested در شئ source زمانی مقدارش true می شود که متد Cancel فراخوانی شود، پس تا زمانی که عملیات Task به اتمام نرسیده و زمانی که کاربر کلید های Ctrl+C را فشار نداده برنامه در حلقه while منتظر می ماند.

در ادامه باید Task ایجاد شده را به صورتی تغییر دهیم که داخل حلقه for بررسی شود که متد Cancel فراخوانی شده است یا خیر، اگر فراخوانی شده بود باید از Task خارج شویم، برای این کار نیز از خصوصیت IsCancellationRequested در شئ source استفاده می کنیم، Task ایجاد شده را به صورت زیر تغییر می دهیم:

Task < int > averageTask = new Task < int > (() = >
{
    Console.WriteLine("Calculating average...");
    Console.WriteLine("Press Ctrl+C to cancel...");
    var sum = 0;
    for (int counter = 1; counter < = 100; counter++)
    {
        if (source.IsCancellationRequested)
        {
            Console.WriteLine("Operation terminated!");
            return 0;
        }
        sum += counter;
        Thread.Sleep(100);
    }
    Console.WriteLine("All done.");
    return sum/100;
}, source.Token);

همانطور که مشاهده می کنید داخل حلقه for گفتیم که اگر IsCancellationRequested برابر true بود پیغامی را نمایش بده و مقدار 0 را برگردان. کد نهایی ما به صورت زیر می باشد:

class Program
{
    private static CancellationTokenSource source = new CancellationTokenSource();
    static void Main(string[] args)
    {
        Task < int > averageTask = new Task < int >(() = >
        {
            Console.WriteLine("Calculating average...");
            Console.WriteLine("Press Ctrl+C to cancel...");
            var sum = 0;
            for (int counter = 1; counter <= 100; counter++) { if (source.IsCancellationRequested) { Console.WriteLine("Operation terminated!"); return 0; } sum += counter; Thread.Sleep(100); } Console.WriteLine("All done."); return sum/100; }, source.Token); averageTask.Start(); Console.CancelKeyPress += (sender, eventArgs) = >
        {
            source.Cancel();
            eventArgs.Cancel = true;
        };
        while (!averageTask.IsCompleted && !source.IsCancellationRequested)
        {                                                                                                
        }
 
        Console.WriteLine(averageTask.Result);
    }
}

در صورتی که برنامه بالا را اجرا کرده و کلید های Ctrl+C را فشار دهیم خروجی زیر برای ما نمایش داده می شود:

Calculating average...
Press Ctrl+C to cancel...
Operation terminated!
0
Press any key to continue . . .

استفاده از CancellationToken در کلاس Parallel

علاوه بر کلاس Task می توان از قابلیت CancellationToken در متدهای کلاس Parallel نیز استفاده کرد، برای آشنایی بیشتر فرض کنید کدی به صورت زیر تعریف شده که لیست فایل های jpg داخل یک پوشه را پردازش می کند:

var jpegFiles = System.IO.Directory.GetFiles("D:\\Images", "*.jpg");
 
Parallel.ForEach(jpegFiles, file = >
{
    var fileInfo = new FileInfo(file);
    // process file
});

برای متوقف کردن عملیات پردازش فایل ها، ابتدا شئ ای از نوع CancellationTokenSource مانند مثال قبل ایجاد می کنیم:

private static CancellationTokenSource source = new CancellationTokenSource();

در قدم بعدی کلاسی از نوع ParallelOptions به صورت زیر تعریف کرده، خصوصیت CancellationToken را برابر خصوصیت Token در شئ source قرار داده و این کلاس را به عنوان پارامتر ورودی به متد ForEach به صورت زیر ارسال می کنیم:

ParallelOptions options = new ParallelOptions();
options.CancellationToken = source.Token;
 
try
{
    Parallel.ForEach(jpegFiles,options, file = >
    {
        options.CancellationToken.ThrowIfCancellationRequested();                                                
        var fileInfo = new FileInfo(file);
        // process file
    });
}
catch (OperationCanceledException ex)
{
    Console.WriteLine(ex);
}

دقت کنید در قسمت ForEach متدی با نام ThrowIfCancellationRequested فراخوانی شده است، در حقیقت این متد بعد از فراخوانی بررسی می کند که آیا متد Cancel برای شئ source فراخوانی شده است یا خیر، اگر فراخوانی شده بود خطایی از نوع OperationCanceledException ایجاد می شود که در خارج از بدنه ForEach کلاس Parallel، بوسیله ساختار try..catch این خطا مدیریت شده است. دقت کنید که روند مدیریت Cancel کردن در کلاس Parallel با کلاس Task متفاوت است و دلیل این موضوع نوع برخورد برنامه با این کلاس ها است. در قسمت بعدی با مبحث Parallel LINQ آشنا خواهیم شد.

منبع



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

 OSD (on screen Display) Menu Setup

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

Menu Setup

DWDR

تصاویر ویدئویی واضح تر و با جزئیات بیشتر. DWDR نقاط سایه ای سیاهی که در تصویر بوجود می آید را از بین می برد این قابلیت در محیط هایی که هم نقاط تیره و هم نقاط روشن دارد بسیار کاربردی است .

DWDR

SMART IR

این ویژگی از بین برنده ی نوردهی بالای چراغ های IR روی دوربین ها در شرایطی است که ممکن است فاصله ی دوربین تا جسم خیلی زیاد نباشد.

SMART IR

(DNR(Digital Noise Redctron

کاهش نویز تصویر در محیط های با نور پایین (تصویر تهیه شده در شب).
دوربین های با امکان DNR در مقایسه با دوربین هایی که امکان DNR ندارند نویز کمتری ایجاد می کنند.

(DNR(Digital Noise Redctron

HLC Highlight Compensation

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

HLC Highlight Compensation

الگوریتم Canny

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

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

 

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

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

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

 

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

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

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

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

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

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

 

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

 

 

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

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

 

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

 

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

Canny in C

رمز فایل : behsanandish.com

الگوریتم Canny

لبه یاب کنی توسط جان اف کنی در سال 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


الگوریتم sobel

کد الگوریتم سوبل – متد سوبل لبه ها را با استفاده از تخمین زدن مشتق پیدا می کند، که لبه ها را در آن نقاطی بر می گرداند که گرادیان تصویر I ، max است. پیشنهاد می کنیم جهت آشنایی با الگوریتم های لبه یابی، مطلب «الگوریتم های لبه یابی و انواع آن» را مشاهده نمایید. در فیلتر سوبل دو ماسک به صورت زیر وجود دارد:

ماسک سوبل عمودی         ماسک سوبل افقی

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

براي مشخص شدن کلیه لبه ها:
اگر Gو Gy تصاویر فیلتر شده به وسیله ماسک افقی و عمودي باشند، آنگاه تصویر  فرمول الگوریتم سوبل    لبه هاي تصویر را بهتر نشان میدهد. روال فوق به عملگر یا الگورریتم سوبل موسوم است.
در عمل، به منظور کاهش هزینه محاسبات، به جاي  فرمول الگوریتم سوبل  میتوان از تقریب [Gx] + [Gy] استفاده میشود. توجه شود که نتیجه این دو فرمول تقریبا یکسان است ولی فرمول دوم با هزینه کمتري قابل محاسبه می باشد.

تأثیر عملگر سوبل بر لبه یابی تصاویر

 

کد الگوریتم سوبل( Sobel ) در Matlab:

 

clc; clear; close all; warning off; 

I=imread('lena.bmp');
I=im2double(I);
I=imnoise(I, 'gaussian', 0, 0.001);
figure(1);
imshow(I);title('org img');

[height width R]=size(I);

for i=2:height-1
    for j=2:width-1
        Dx(i,j)=[I(i+1,j-1)-I(i-1,j-1)]+2*[I(i+1,j)-I(i-1,j)]+[I(i+1,j+1)-I(i-1,j+1)];
        Dy(i,j)=[I(i-1,j+1)-I(i-1,j-1)]+2*[I(i,j+1)-I(i,j-1)]+[I(i+1,j+1)-I(i+1,j-1)];
        S(i,j)=sqrt(Dx(i,j)^2+Dy(i,j)^2);
        if Dx(i,j)&amp;lt;1
            Dx(i,j)=0;
        else Dx(i,j)=1;
        end
        if Dy(i,j)&amp;lt;1
            Dy(i,j)=0;
        else Dy(i,j)=1;
        end
    end
end
figure(2);
imshow(Dx,[]);

figure(3);
imshow(Dy,[]);

for i=1:255
    for j=1:255
       if (S(i,j)&amp;lt;1)
            S(i,j)=0;
        else S(i,j)=1;
        end
    end
end
figure(4);
imshow(S,[]);
دانلود کد فوق از طریق لینک زیر:

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

رمز فایل : behsanandish.com


 

کد الگوریتم سوبل( Sobel ) در #C:

1.کد برای فیلتر کانولوشن: بخش اول این تابع برای گرفتن اطلاعات تصویر و ذخیره آن به آرایه اختصاص داده شده است.

 

private static Bitmap ConvolutionFilter(Bitmap sourceImage, 
    double[,] xkernel, 
    double[,] ykernel, double factor = 1, int bias = 0, bool grayscale = false)
{

    //Image dimensions stored in variables for convenience
    int width = sourceImage.Width;
    int height = sourceImage.Height;

     //Lock source image bits into system memory
    BitmapData srcData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

     //Get the total number of bytes in your image - 32 bytes per pixel x image width x image height -&amp;gt; for 32bpp images
    int bytes = srcData.Stride * srcData.Height;

     //Create byte arrays to hold pixel information of your image
    byte[] pixelBuffer = new byte[bytes];
    byte[] resultBuffer = new byte[bytes];

     //Get the address of the first pixel data
    IntPtr srcScan0 = srcData.Scan0;

     //Copy image data to one of the byte arrays
    Marshal.Copy(srcScan0, pixelBuffer, 0, bytes);

    //Unlock bits from system memory -&amp;gt; we have all our needed info in the array
    sourceImage.UnlockBits(srcData);

 

2.کد تبدیل سیاه و سفید: از آنجایی که اپراتور Sobel اغلب برای تصاویر سیاه و سفید استفاده می شود، در اینجا یک کد برای تبدیل به سیاه و سفید است که توسط پارامتر boolean شما می توانید انتخاب کنید تبدیل کردن را یا نه.

 

//Convert your image to grayscale if necessary
if (grayscale == true)
{
    float rgb = 0;
    for (int i = 0; i &amp;lt; pixelBuffer.Length; i += 4)
    {
        rgb = pixelBuffer[i] * .21f;
        rgb += pixelBuffer[i + 1] * .71f;
        rgb += pixelBuffer[i + 2] * .071f;
        pixelBuffer[i] = (byte)rgb;
        pixelBuffer[i + 1] = pixelBuffer[i];
        pixelBuffer[i + 2] = pixelBuffer[i];
        pixelBuffer[i + 3] = 255;
    }
}

 

3.کد برای تنظیم متغیرهای مورد استفاده در فرآیند کانولوشن:

 

/Create variable for pixel data for each kernel
double xr = 0.0;
double xg = 0.0;
double xb = 0.0;
double yr = 0.0;
double yg = 0.0;
double yb = 0.0;
double rt = 0.0;
double gt = 0.0;
double bt = 0.0;

//This is how much your center pixel is offset from the border of your kernel
//Sobel is 3x3, so center is 1 pixel from the kernel border
int filterOffset = 1;
int calcOffset = 0;
int byteOffset = 0;

//Start with the pixel that is offset 1 from top and 1 from the left side
//this is so entire kernel is on your image
for (int OffsetY = filterOffset; OffsetY &amp;lt; height - filterOffset; OffsetY++)
{
    for (int OffsetX = filterOffset; OffsetX &amp;lt; width - filterOffset; OffsetX++)
    {
        //reset rgb values to 0
        xr = xg = xb = yr = yg = yb = 0;
        rt = gt = bt = 0.0;

        //position of the kernel center pixel
        byteOffset = OffsetY * srcData.Stride + OffsetX * 4;

 

4. اعمال کانولوشن هسته به پیکسل فعلی:

 

//kernel calculations
for (int filterY = -filterOffset; filterY &amp;lt;= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX &amp;lt;= filterOffset; filterX++)
{
calcOffset = byteOffset + filterX * 4 + filterY * srcData.Stride;
xb += (double)(pixelBuffer[calcOffset]) * xkernel[filterY + filterOffset, filterX + filterOffset];
xg += (double)(pixelBuffer[calcOffset + 1]) * xkernel[filterY + filterOffset, filterX + filterOffset];
xr += (double)(pixelBuffer[calcOffset + 2]) * xkernel[filterY + filterOffset, filterX + filterOffset];
yb += (double)(pixelBuffer[calcOffset]) * ykernel[filterY + filterOffset, filterX + filterOffset];
yg += (double)(pixelBuffer[calcOffset + 1]) * ykernel[filterY + filterOffset, filterX + filterOffset];
yr += (double)(pixelBuffer[calcOffset + 2]) * ykernel[filterY + filterOffset, filterX + filterOffset];
}
}

//total rgb values for this pixel
bt = Math.Sqrt((xb * xb) + (yb * yb));
gt = Math.Sqrt((xg * xg) + (yg * yg));
rt = Math.Sqrt((xr * xr) + (yr * yr));

//set limits, bytes can hold values from 0 up to 255;
if (bt &amp;gt; 255) bt = 255;
else if (bt &amp;lt; 0) bt = 0;
if (gt &amp;gt; 255) gt = 255;
else if (gt &amp;lt; 0) gt = 0;
if (rt &amp;gt; 255) rt = 255;
else if (rt &amp;lt; 0) rt = 0;

//set new data in the other byte array for your image data
resultBuffer[byteOffset] = (byte)(bt);
resultBuffer[byteOffset + 1] = (byte)(gt);
resultBuffer[byteOffset + 2] = (byte)(rt);
resultBuffer[byteOffset + 3] = 255;
}
}

 

5. کد خروجی تصویر پردازش شده:

 

//Create new bitmap which will hold the processed data
    Bitmap resultImage = new Bitmap(width, height);

    //Lock bits into system memory
    BitmapData resultData = resultImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    //Copy from byte array that holds processed data to bitmap
    Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);

    //Unlock bits from system memory
    resultImage.UnlockBits(resultData);

    //Return processed image
    return resultImage;
}

 

6. کد برای هسته سوبل:

 

//Sobel operator kernel for horizontal pixel changes
private static double[,] xSobel
{
    get
    {
        return new double[,]
        {
            { -1, 0, 1 },
            { -2, 0, 2 },
            { -1, 0, 1 }
        };
    }
}

//Sobel operator kernel for vertical pixel changes
private static double[,] ySobel
{
    get
    {
        return new double[,]
        {
            {  1,  2,  1 },
            {  0,  0,  0 },
            { -1, -2, -1 }
        };
    }
}

 

همه این کد در اینجا موجود است (پروژه با ویژوال استودیو 2015 ایجاد شد):

SobelOperatorInC#

رمز فایل : behsanandish.com

 

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

 


 

کد الگوریتم سوبل( Sobel ) در ++C:

در ادامه دو کد برای الگوریتم Sobel  در ++C آماده کردیم:

1.

 

#include&lt;iostream&gt;
#include&lt;cmath&gt;
#include&lt;opencv2/imgproc/imgproc.hpp&gt;
#include&lt;opencv2/highgui/highgui.hpp&gt;

using namespace std;
using namespace cv;

 
// Computes the x component of the gradient vector
// at a given point in a image.
// returns gradient in the x direction
int xGradient(Mat image, int x, int y)
{
    return image.at&lt;uchar&gt;(y-1, x-1) +
                2*image.at&lt;uchar&gt;(y, x-1) +
                 image.at&lt;uchar&gt;(y+1, x-1) -
                  image.at&lt;uchar&gt;(y-1, x+1) -
                   2*image.at&lt;uchar&gt;(y, x+1) -
                    image.at&lt;uchar&gt;(y+1, x+1);
}

// Computes the y component of the gradient vector
// at a given point in a image
// returns gradient in the y direction

int yGradient(Mat image, int x, int y)
{
    return image.at&lt;uchar&gt;(y-1, x-1) +
                2*image.at&lt;uchar&gt;(y-1, x) +
                 image.at&lt;uchar&gt;(y-1, x+1) -
                  image.at&lt;uchar&gt;(y+1, x-1) -
                   2*image.at&lt;uchar&gt;(y+1, x) -
                    image.at&lt;uchar&gt;(y+1, x+1);
}

int main()
{

      Mat src, dst;
      int gx, gy, sum;

      // Load an image
      src = imread(&quot;lena.jpg&quot;, CV_LOAD_IMAGE_GRAYSCALE);
      dst = src.clone();
      if( !src.data )
      { return -1; }

        for(int y = 0; y &lt; src.rows; y++)
            for(int x = 0; x &lt; src.cols; x++)
                dst.at&lt;uchar&gt;(y,x) = 0.0;

        for(int y = 1; y &lt; src.rows - 1; y++){
            for(int x = 1; x &lt; src.cols - 1; x++){
                gx = xGradient(src, x, y);
                gy = yGradient(src, x, y);
                sum = abs(gx) + abs(gy);
                sum = sum &gt; 255 ? 255:sum;
                sum = sum &lt; 0 ? 0 : sum;
                dst.at&lt;uchar&gt;(y,x) = sum;
            }
        }

        namedWindow(&quot;final&quot;);
        imshow(&quot;final&quot;, dst);

        namedWindow(&quot;initial&quot;);
        imshow(&quot;initial&quot;, src);

      waitKey();

 
    return 0;
}

 

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

Sobel in C++-Code1

رمز فایل : behsanandish.com

 

2.

 

#include &quot;itkImage.h&quot;
#include &quot;itkImageFileReader.h&quot;
#include &quot;itkImageFileWriter.h&quot;
#include &quot;itkSobelEdgeDetectionImageFilter.h&quot;

int main( int argc, char* argv[] )
{
  if( argc != 3 )
    {
    std::cerr &lt;&lt; &quot;Usage: &quot;&lt;&lt; std::endl;
    std::cerr &lt;&lt; argv[0];
    std::cerr &lt;&lt; &quot;&lt;InputFileName&gt; &lt;OutputFileName&gt;&quot;;
    std::cerr &lt;&lt; std::endl;
    return EXIT_FAILURE;
    }

  constexpr unsigned int Dimension = 2;

  using InputPixelType = unsigned char;
  using InputImageType = itk::Image&lt; InputPixelType, Dimension &gt;;

  using ReaderType = itk::ImageFileReader&lt; InputImageType &gt;;
  ReaderType::Pointer reader = ReaderType::New();
  reader-&gt;SetFileName( argv[1] );

  using OutputPixelType = float;
  using OutputImageType = itk::Image&lt; OutputPixelType, Dimension &gt;;

  using FilterType = itk::SobelEdgeDetectionImageFilter&lt; InputImageType, OutputImageType &gt;;
  FilterType::Pointer filter = FilterType::New();
  filter-&gt;SetInput( reader-&gt;GetOutput() );

  using WriterType = itk::ImageFileWriter&lt; OutputImageType &gt;;
  WriterType::Pointer writer = WriterType::New();
  writer-&gt;SetFileName( argv[2] );
  writer-&gt;SetInput( filter-&gt;GetOutput() );

  try
    {
    writer-&gt;Update();
    }
  catch( itk::ExceptionObject &amp; error )
    {
    std::cerr &lt;&lt; &quot;Error: &quot; &lt;&lt; error &lt;&lt; std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

 

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

Sobel in C++-Code2

رمز فایل : behsanandish.com

 


کد الگوریتم سوبل( Sobel ) در C:

 

/* sobel.c */
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;float.h&gt;
#include &quot;mypgm.h&quot;

void sobel_filtering( )
     /* Spatial filtering of image data */
     /* Sobel filter (horizontal differentiation */
     /* Input: image1[y][x] ---- Outout: image2[y][x] */
{
  /* Definition of Sobel filter in horizontal direction */
  int weight[3][3] = {{ -1,  0,  1 },
      { -2,  0,  2 },
      { -1,  0,  1 }};
  double pixel_value;
  double min, max;
  int x, y, i, j;  /* Loop variable */

  /* Maximum values calculation after filtering*/
  printf(&quot;Now, filtering of input image is performed\n\n&quot;);
  min = DBL_MAX;
  max = -DBL_MAX;
  for (y = 1; y &lt; y_size1 - 1; y++) {
    for (x = 1; x &lt; x_size1 - 1; x++) {
      pixel_value = 0.0;
      for (j = -1; j &lt;= 1; j++) {
    for (i = -1; i &lt;= 1; i++) {
      pixel_value += weight[j + 1][i + 1] * image1[y + j][x + i];
    }
      }
      if (pixel_value &lt; min) min = pixel_value;
      if (pixel_value &gt; max) max = pixel_value;
    }
  }
  if ((int)(max - min) == 0) {
    printf(&quot;Nothing exists!!!\n\n&quot;);
    exit(1);
  }

  /* Initialization of image2[y][x] */
  x_size2 = x_size1;
  y_size2 = y_size1;
  for (y = 0; y &lt; y_size2; y++) {
    for (x = 0; x &lt; x_size2; x++) {
      image2[y][x] = 0;
    }
  }
  /* Generation of image2 after linear transformtion */
  for (y = 1; y &lt; y_size1 - 1; y++) {
    for (x = 1; x &lt; x_size1 - 1; x++) {
      pixel_value = 0.0;
      for (j = -1; j &lt;= 1; j++) {
    for (i = -1; i &lt;= 1; i++) {
      pixel_value += weight[j + 1][i + 1] * image1[y + j][x + i];
    }
      }
      pixel_value = MAX_BRIGHTNESS * (pixel_value - min) / (max - min);
      image2[y][x] = (unsigned char)pixel_value;
    }
  }
}

main( )
{
  load_image_data( );   /* Input of image1 */ 
  sobel_filtering( );   /* Sobel filter is applied to image1 */
  save_image_data( );   /* Output of image2 */
  return 0;
}
دانلود کد فوق از طریق لینک زیر:

Sobel in C

رمز فایل : behsanandish.com


 

کد الگوریتم سوبل( Sobel ) در Visual Basic:

 

Private Sub bEdge_Click(sender As Object, e As EventArgs) _
Handles bEdge.Click

'Sobel Edge'
Dim tmpImage As Bitmap = New Bitmap(picOriginal.Image)
Dim bmpImage As Bitmap = New Bitmap(picOriginal.Image)

Dim intWidth As Integer = tmpImage.Width
Dim intHeight As Integer = tmpImage.Height

Dim intOldX As Integer(,) = New Integer(,) {{-1, 0, 1}, _
{-2, 0, 2}, {-1, 0, 1}}
Dim intOldY As Integer(,) = New Integer(,) {{1, 2, 1}, _
{0, 0, 0}, {-1, -2, -1}}

Dim intR As Integer(,) = New Integer(intWidth - 1, _
intHeight - 1) {}
Dim intG As Integer(,) = New Integer(intWidth - 1, _
intHeight - 1) {}
Dim intB As Integer(,) = New Integer(intWidth - 1, _
intHeight - 1) {}

Dim intMax As Integer = 128 * 128

For i As Integer = 0 To intWidth - 1

For j As Integer = 0 To intHeight - 1

intR(i, j) = tmpImage.GetPixel(i, j).R
intG(i, j) = tmpImage.GetPixel(i, j).G
intB(i, j) = tmpImage.GetPixel(i, j).B

Next

Next

Dim intRX As Integer = 0
Dim intRY As Integer = 0
Dim intGX As Integer = 0
Dim intGY As Integer = 0
Dim intBX As Integer = 0
Dim intBY As Integer = 0

Dim intRTot As Integer
Dim intGTot As Integer
Dim intBTot As Integer

For i As Integer = 1 To tmpImage.Width - 1 - 1

For j As Integer = 1 To tmpImage.Height - 1 - 1

intRX = 0
intRY = 0
intGX = 0
intGY = 0
intBX = 0
intBY = 0

intRTot = 0
intGTot = 0
intBTot = 0

For width As Integer = -1 To 2 - 1

For height As Integer = -1 To 2 - 1

intRTot = intR(i + height, j + width)
intRX += intOldX(width + 1, height + 1) * intRTot
intRY += intOldY(width + 1, height + 1) * intRTot

intGTot = intG(i + height, j + width)
intGX += intOldX(width + 1, height + 1) * intGTot
intGY += intOldY(width + 1, height + 1) * intGTot

intBTot = intB(i + height, j + width)
intBX += intOldX(width + 1, height + 1) * intBTot
intBY += intOldY(width + 1, height + 1) * intBTot

Next

Next

If intRX * intRX + intRY * intRY &gt; intMax OrElse
intGX * intGX + intGY * intGY &gt; intMax OrElse
intBX * intBX + intBY * intBY &gt; intMax Then

bmpImage.SetPixel(i, j, Color.Black)

Else

bmpImage.SetPixel(i, j, Color.Transparent)

End If

Next

Next

picModified.Image = bmpImage

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

Sobel in VB

رمز فایل : behsanandish.com

 

کد الگوریتم sobel در VB

 


 

پیشنهاد می کنیم جهت آشنایی با الگوریتم های لبه یابی، مطلب «الگوریتم های لبه یابی و انواع آن» را مشاهده نمایید.

 

بازشناخت الگو

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

تشخیص خودکار چهره شخص با استفاده از الگوریتم‌های تشخیص الگو

تشخیص خودکار چهره شخص با استفاده از الگوریتم‌های تشخیص الگو

انواع تشخیص الگو

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

تشخیص آیتم‌های واقعی

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

تشخیص الگوهای زمانی و فضایی

این فرایند تشخیص، شناسایی و دسته‌بندی الگوهای فضایی و الگوهای زمانی را در بر می‌گیرد. مثال‌هایی از الگوهای فضایی کارکترها، اثر انگشت‌ها، اشیاء فیزیکی و تصاویر هستند. الگوهای زمانی شامل فرم‌های موجی گفتار، سری‌های زمانی و … است.

الگوها و کلاس‌های الگوها

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

مسائل اساسی در طراحی سیستم تشخیص الگو

به طور کلی طراحی یک سیستم تشخیص الگو چندین مسئلهٔ اصلی را در بر می‌گیرد: ۱)طریقه نمایش داده‌ها ۲)استخراج ویژگی ۳)تعیین رویه تصمیم بهینه

طریقه نمایش داده‌ها

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

استخراج ویژگی

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

تعیین رویه تصمیم بهینه

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

یادگیری و تمرین دادن

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

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

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

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

کلیات یک سیستم تشخیص الگو

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

الگوریتم‌ها

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

الگوریتم‌های کلاس بندی (الگوریتم‌های با ناظر پیشگو)

  • درخت تصمیم و لیست تصمیم
  • ماشین‌های بردار پشتیبانی
  • شبکه‌های عصبی
  • پرسپترونن
  • k-نزدیکترین همسایگی

الگوریتم‌های خوشه ساری (الگوریتم‌های بدون ناظر پیشگو)

  • مدل‌های دسته‌بندی ترکیبی
  • خوشه سازی سلسله مراتبی
  • Kernel PCA

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

با ناظر

  • رگرسیون خطی
  • شبکه‌های عصبی
  • Gaussian process regression

بدون ناظر

  • Principal Components Analysis= PCA
  • LCA

کاربردها

منبع

اصلاح اثر مخرب مژه‌ها بر تصاویر عنبیه به کمک فیلتر میانه با قاب افقی

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

كليد واژه- تشخیص عنبیه ، حذف مژه ، شبکه عصبی ، فیلتر میانه با قاب افقی

 

1-     مقدمه

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

مراحل انجام عمل تشخیص هویت به کمک عنبیه شامل موارد زیر است: (شکل 1)

  1. تصویربرداری
  2. قطعه‌بندی
  3. نرمال سازی
  4. استخراج ویژگی
  5. تطابق

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

جزئیات مراحل تشخیص عنبیه بیان‌شده توسط دکتر جان داگمن

جزئیات مراحل تشخیص عنبیه بیان‌شده توسط دکتر جان داگمن

 

نویسندگان : محمد مهدی ابراهیمی، ناصر قاسم آقایی و حسین ابراهیم پور

تعداد صفحات : 6 صفحه

سال انتشار: 1392

پسورد : behsanandish.com

دانلود رایگان : مقاله-اصلاح اثر مخرب مژه‌ها بر تصاویر عنبیه به کمک فیلتر میانه با قاب افقی

پردازش سیگنال چیست؟

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

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

تاریخچه

با توجه به الن وی. اوپنهایم و رنالد شافر ،اصول اولیه پردازش سیگنال در روش های آنالیز عددی کلاسیک در قرن 17 یافت می شود. اوپنهایم و شافر اشاره کرده اند که “دیجیتالی ساختن” یا پالایش دیجیتالی این روش ها درسیستم های کنترلی در سال های 1940 و 1950 می توان یافت.

 

زمینه های کاربرد

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

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

  • مدل اتصال متقابل سامانه های باز، 1 در هفت لایه مدل OSI ، لایه فیزیکی (مدولاسیون،برابر سازی ، هم تافتنو غیره.);
  • OSI لایه 2 لایه پیوند داده ای ;
  • OSI 6، لایه لایه نمایش ( برنامه نویسی از جمله تبدیل آنالوگ به دیجیتالو فشرده سازی سیگنال).

 

دستگاه های معمولی

  • فیلتر – برای مثال آنالوگ (منفعل یا فعال) یا دیجیتال (FIRهای IIR, دامنه فرکانسی یا کنترل تصادفی و غیره.)
  • نمونه برداری و مبدل سیگنال های آنالوگ به دیجیتال برای فراگیری سیگنالها و بازسازی آن ها، که شامل اندازه گیری فیزیکی سیگنال ها،ذخیره سازی یا انتقال آن را به عنوان سیگنال دیجیتال و بازسازی سیگنال اصلی یا تقریبی آن در کاربرد های احتمالی بعدی.
  • فشرده سازی داده ها
  • پردازشگر سیگنال دیجیتال سیگنال دیجیتال (DSPs)

 

روش های ریاضی کاربردی

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

 

دسته بندی ها

پردازش سیگنال آنالوگ

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

 

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

پردازش سیگنال های پیوسته در زمان برای سیگنال های است که با تغییرات پیوسته دامنه تغییر می کنند(بدون در نظر گرفتن بعضی نقاط منقطع)

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

 

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

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

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

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

 

پردازش سیگنال دیجیتال

پردازش سیگنال های دیجیتال پردازش نمونه های سیگنال در زمان های گسسته می باشد. پردازش توسط , رایانه ها یا با مدارهای دیجیتال همچون ASIC ها ، field-FPGA ها ،یا پردازنده های سیگنال دیجیتال ها انجام می گیرد . عملیات های معمول ریاضی شامل نمایش نقطه ثابت ، ممیز شناور ، مقدار حقیقی یا مختلط اعداد ، ضرب و جمع از این جمله می باشند. بعضی دیگر از عملیات های معمول توسط دایره بافر و جدول های look-up توسط سخت افزار پشتیبانی می شوند. مثال های از این الگوریتم ها تبدیل فوریه سریع (FFT) ، فیلتر های FIR ، فیلتر های IIR ، و فیلتر تطبیقیمی باشند.

 

غیر خطی پردازش سیگنال های غیرخطی

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

منبع


پردازش سیگنال چیست؟ (Signal processing)

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

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

 

مثال ها

شکل زیر سیگنال صوتی را نشان می‌دهد که هنگام فشار دادن کلید ۱ بر روی تلفن تولید می‌شود:

 

شکل 1-سیگنال پیوسته

 

شکل 2-سیگنال گسسته

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

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

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

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

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

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

 

تبدیل آنالوگ به دیجیتال

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

• آماده کردن سیگنال ورودی
• فیلتر کردن سیگنال ورودی
• نمونه برداری
• چندی کردن
شماتیک زیر نیز فرآیند تبدیل سیگنال آنالوگ به دیجیتال را نشان می‌دهد:

 

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

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

در  مراحل بعدی عمل دیجیتال کردن زمانی که از مبدل آنالوگ به دیجیتال استفاده  می‌کنیم، عملا نیاز به ولتاژهایی در بازه ( 5..0) ، ( 5-..5 ) یا … ولت نیاز  داریم. اما  همانطور که دیدیم خروجی برخی از سنسورها ( همانند سنسور نوار قلبی ) در حد  میلی ولت  است. بنابراین نیاز به روشی داریم که بتوانیم خروجی سنسورها را تقویت کرده و  آن‌ها  را برای ورود به مبدل‌های آنالوگ به دیجیتال آماده کنیم.

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

 

فیلتر کردن سیگنال

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

 

 طراحی فیلترها

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

 

نمونه برداری کردن

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

 

چندی کردن سیگنال

در  مرحله نمونه برداری دیدیم که یک نمونه از سیگنال به شکل ولتاژ نمونه برداری  شد. در  این مرحله ولتاز نمونه برداری شده باید به شکل دیجیتالی ( عدد باینری )  تبدیل شود.  برای این منظور نیز از مبدل‌های آنالوگ به دیجیتال استفاده می‌کنیم. به  عنوان مثال  یک مبدل انالوگ به دیجیتال 8 بیتی به ازای ورودی خود عددی بین 0 تا 255  تولید می‌ کند. فرض کنید ورودی مبدل در بازه 0 تا 5 ولت باشد. این بدان معناست که به  ازای ورودی 0 ولت ، خروجی مبدل عدد باینری 0 و به ازای ورودی 5 ولت خروجی مبدل  عدد باینری 255 خواهد بود. بدیهی است که افزایش تعداد بیت‌های مبدل موجب افزایش  دقت چندی  شدن خواهد شد.