زمانی که یک Thread جدید در برنامه های دات نت ایجاد می شوند، این Thread ها می توانند به دو صورت Foreground و Background اجرا شوند:
Thread های Foreground: زمانی که کی Thread در حالت Foreground اجرا می شود باعث می شود که Thread اصلی برنامه تا زمان کامل شدن اجرای Thread ایجاد شده در حالت اجرا بماند. یعنی از Shut-down شدن Primary Thread توسط CLR جلوگیری می شود.
Thread های Background: این Thread ها که با نام Daemon Thread شناخته می شوند به CLR می گوید که اجرای این Thread آنقدر اهمیت ندارد که Thread اصلی برنامه بخواهد منتظر بماند تا عملیات آن به اتمام برسد و می تواند در هر زمان که Thread اصلی برنامه به اتمام رسید، به صورت خودکار Thread های Background را نیز از بین ببرد.
توجه کنید که کلیه Thread هایی که در برنامه ها ایجاد می کنیم به صورت پیش فرض در حالت Foreground قرار دارند. برای آشنایی بیشتر با این موضوع نمونه کد زیر را در نظر بگیرید:
static void Main(string[] args)
{
var thread = new Thread(PrintNumbers);
thread.Start();
}
public static void PrintNumbers()
{
for (int counter = 1; counter < 10; counter++)
{
Console.WriteLine(counter);
Thread.Sleep(200);
}
}
همانطور که گفتیم Thread ایجاد شده به صورت پیش فرض از نوع Foreground است و به همین دلیل تا زمانی که روند اجرای Thread ایجاد شده به اتمام نرسد از برنامه خارج نمی شویم و کلیه اعداد در خروجی چاپ می شوند. اما در کد زیر Thread ایجاد شده به صورت Background است و خواهیم دید که پس از اجرای برنامه به دلیل اینکه Thread اصلی زودتر از Thread ایجاد شده به اتمام می رسد، CLR به صورت خودکار Thread ایجاد شده را از بین می برد و اعداد به صورت کامل در خروجی نمایش داده نمی شوند:
static void Main(string[] args)
{
var thread = new Thread(PrintNumbers);
thread.IsBackground = true;
thread.Start();
}
public static void PrintNumbers()
{
for (int counter = 1; counter < 10; counter++)
{
Console.WriteLine(counter);
Thread.Sleep(200);
}
}
در برنامه های واقعی باید با دقت نوع Thread ها را انتخاب کرد، برای مثال فرض کنید که در برنامه شما در یک Thread جداگانه عملیاتی بر روی داده های بانک اطلاعاتی انجام می شود و نتیجه این عملیات در انتها باید در جایی ذخیره شود، می توانید برای اینکار یک Thread از نوع Foreground ایجاد کرده تا پس از خروج از برنامه، Thread اصلی منتظر اتمام انجام عملیات شده و سپس عملیات خروج کامل انجام شود. در مبحث بعدی در مورد موضوع همزمانی یا Concurrency صحبت می کنیم که از مشکلات اساسی در زمینه برنامه نویسی asynchronous می باشد و در مورد راهکار های حل این مشکل نیز صحبت خواهیم کرد.
برای پی بردن به این موضوع که کدامیک از دو تکنولوژی CCD یا CMOS بهتر است و تصویر بهتری در اختیار کاربر قرار میدهد، لازم است تا ابتدا نحوه ی عملکرد هرکدام از این تکنولوژی ها را فرا بگیریم :
CCD چطور کار میکند ؟
همانطور که در شکل مشاهده میفرمایید، CCD دارای دو بخش اصلی مجزا از هم می باشد، بخش نارنجی رنگ که پیکسل ها را مشخص میکند و در حقیقت همان فتودیود است و بخش آبی رنگ که ترانزیستورهای مختلف، آی سی سیستم را تشکیل می دهند. بدلیل جدا بودن فتودیود از ترانزیستورها، امکان جذب نور در این تکنولوژی در حالت حداکثر ممکن میباشد و با توجه به اینکه از یک آی سی جداگانه برای پردازش تصاویر تهیه شده توسط فتودیودها استفاده میشود هم میتوان امکانات بیشتری ( از جمله الگوریتم های حذف نویز، تشخیص چهره، تشخیص حرکت و … ) را به سیستم تصویربرداری اضافه کرد.
CMOS چطور کار میکند ؟
تفاوت اصلی تکنولوژی CMOS با CCD همانطور که در شکل کاملا مشخص می باشد در این است که فتودیودها و ترانزیستورها در تکنولوژی CMOS بر روی یک برد سوار میشوند.
این روش باعث پایین آمدن مصرف انرژی نسبت به CCD و کمترشدن قیمت تمام شده ی محصول تهیه شده با تکنولوژی CMOS می شود ولی با توجه به کیفیت پایین تر و حساسیت به نور کمتر نمیتوان گفت که دوربین هایی که از تکنولوژی CMOS استفاده میکنند کارایی بهتری نسبت به دوربین های مشابه با تکنولوژی CCD دارند.
مقایسه CCD با CMOS :
در شکل بالا تصاویر تهیه شده توسط دو دوربین با لنزها و شرایط نوردهی یکسان توسط CCD و CMOS نمایش داده شده است. همانطور که در شکل ملاحظه می فرمایید تصاویر تهیه شده با CCD از نویز کمتری برخوردار بوهد و رنگ ها شفافیت و واقعیت بیشتری دارند.
بطور کلی اگر بخواهیم این دو تکنولوژی را با هم مقایسه کنیم میتوانیم به موارد زیر توجه کنیم : • دوربین های CMOS حساسیت به نور کمتری دارند، لذا تصاویر دریافت شده از کیفیت پایینتری نسبت به تکنولوژی همرده در دوربین های CCD برخوردار است. • دوربین های CMOS حساسیت بیشتری به نویز دارند. • دوربین های CMOS در هنگام استفاده از سیستم “دید در شب” چون از راهکار “مادون قرمز” استفاده میشود که نور تک رنگ با قدرت کم در محیط وجود دارد، تصاویر مناسبی ارائه نمیدهند. • دوربین های CMOS ارزانتر از دوربین های CCD میباشند و این به دلیل عدم استفاده از یک بورد جداگانه برای عمل پردازش تصویر می باشد. • دوربین های CMOS مصرف انرژی کمتری نسبت به دوربین های CCD دارند و درصورتی که شما از سیستم های امنیت تصویری با باتری یا سیستم پشتیبان ( یو پی اس ) استفاده میکنید بکاربردن دوربین های CMOS به صرفه تر است. • قابلیت های دوربین های CMOS برای تشخیص چهره، حرکت و … پایین تر از دوربین های CCD می باشد. • دوربین های CCD با سرعت بیشتری عمل Shuttering را انجام می دهند به این معنی که عمل تصویربرداری از اجسام متحرک بهتر و واضح تر انجام می شود.
https://behsanandish.com/wp-content/uploads/تفاوت-CCD-و-CMOS.png331482م. دلیریhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngم. دلیری2020-04-03 10:00:342019-11-30 10:18:43CCD بهتر است یا CMOS ؟
در دوربین های مداربسته ی آنالوگ، رزولوشن با TV Line اندازه گیری می شود. در اکثر دوربین های آنالوگ جدید، TVL بین ۴۲۰ تا ۷۰۰ متغیر است. اگر چه ۴۲۰TVL پایین ترین محسوب می شود، سیستم های امنیت تصویری وجود دارد که لازم است فقط یک فاصله ی کوتاه پوشش داده شود و در اینگونه موارد دوربین های ۴۲۰TVL خوب کار می کنند.
دوربین های ۶۰۰TVL به بالا، تصاویر دقیق تر و کنتراست بالایی دارند. اگر نیاز به تصویر صاف و دقیق دارید، دوربین های بالای ۶۰۰TVL را انتخاب کنید هر چند فاکتور های مهم دیگری مانند میزان لوکس، WDR و … در تصویر دریافتی از دوربین تاثیر گذار هستند.
وقتی دوربین ها تصاویر را دریافت می کنند، آن را از طریق کابل به DVR می فرستند. در DVR، تصاویر از حالت آنالوگ به حالت دیجیتال تبدیل می شوند تا هم روی هارد ذخیره شوند و هم از طریق مانیتور نمایش داده شوند. این مهمترین بخش زنجیره ی دریافت و ذخیره ی تصاویر در دوربین های مدار بسته است.
بهترین دوربین ها با بالاترین کیفیت هم در صورتی که از یک DVR با توانایی تبدیل پایین استفاده شود نمی تواند تصویر خوبی به شما بدهد. DVR ها دو مدل رزولوشن پرکاربرد دارند : D1 , Cif
Cif سایز ۳۲۰*۲۴۰ پیکسل به شما می دهد و D1 سایز ۷۲۰*۴۸۰ و همانطور که مشخص است، D1 چهار برابر بزرگتر از Cif است پس تصویر کمتر فشرده می شود و در نتیجه جزئیات بیشتری را نشان می دهد.
برای انتخاب DVR، رزولوشن ضبط تصاویر را در نظر داشته باشید. بعضی از DVR ها هر دو گزینه را دارند ولی معمولا تعداد فریم بر ثانیه را پایین می آورد تا تصاویر پر کیفیت تری ارائه دهد.
دوربین های آی پی (IP) قابلیت این را دارند که تصاویر با رزولوشن خیلی خیلی بالاتر داشته باشند. اولین چیزی که باید در مورد دوربین های آی پی بدانید این است که تصاویر را به صورت دیجیتال دریافت می کند به همین خاطر نیازی به تبدیل یا فشرده سازی وجود ندارد. دومین مسئله این است که دوربین های آی پی تصاویر را از طریق کابل های Ca45 یا Ca46 منتقل می کند که قابلیت گذردهی خیلی بیشتری دارند.
دوربین های ۱٫۳ مگاپیکسل معمولا کوچکترین رزولوشن دوربین های آی پی است( البته رزولوشن های پایین تر هم وجود دارد) که خیلی خیلی بزرگتر و با کیفیت تر از هر دوربین آنالوگ دیگری است.
با این وجود اگر حتی تصاویر ۱۲۸۰*۱۰۲۴ پیکسل هم برایتان کوچک است می توانید از دوربین های ۳ مگا پیکسل که رزولوشن ۲۰۴۸*۱۵۳۶ پیکسل دارند استفاده کنید یا حتی اگر این هم کم است می توانید دوربین های ۵ مگا پیکسل (۱۹۴۴*۲۵۹۲) استفاده کنید.
خلاصه اینکه با دوربین های آنالوگ و استفاده از DVR شما نهایت تصاویر D1, 4Cif, 2Cif ,Cif خواهید داشت که ممکن است بتوانند نیازهای شما را برآورده کنند. ولی اگر نیاز به رزولوشن بالاتری دارید باید دوربین های آی پی را در نظر بگیرید و از آنها استفاده کنید.
https://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.png00محمد مهدی ابراهیمیhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngمحمد مهدی ابراهیمی2020-03-30 10:00:492019-11-30 10:14:20اولین مطالب بزودی منتشر خواهد شد
ﺷﺮﮐﺖ ﻫﺎی ﺗﻮﻟﯿﺪ ﮐﻨﻨﺪه ﭘﺮدازﺷﮕﺮ ﺑﺮای اﻓﺰاﯾﺶ ﺳﺮﻋﺖ ﭘﺮدازﻧﺪه ﻣﺠﺒﻮر ﺑﻪ ﺑﺎﻻﺑﺮدن ﻓﺮﮐﺎﻧﺲ ﭘﺮدازﺷﮕﺮ ﺑﻮدﻧﺪ. ﯾﮏ راه اﻓﺰاﯾﺶ وﻟﺘﺎژ ﻣﺼﺮﻓﯽ ﭘﺮدازﻧﺪه ﺑﻮد ﮐﻪ دارای ﻧﻘﺎط ﺿﻌﻔﯽ ﻣﺎﻧﻨﺪ اﻓﺰاﯾﺶ دﻣﺎ و اﻓﺰاﯾﺶ ﻣﺼﺮف ﺑﺎﻃﺮی ﻧﯿﺰ ﺑﻮد. از ﻃﺮﻓﯽ ﺗﻮﻟﯿﺪ ﮐﻨﻨﺪﮔﺎن ﭘﺮدازﻧﺪه ﺑﻪ ﮐﻤﮏ ﺑﺮﻧﺎﻣﻪ ﻧﻮﯾﺴﺎن ﭘﯽ ﺑﻪ ﺑﯿﮑﺎری زﯾﺎد ﭘﺮدازﺷﮕﺮﻫﺎ در زﻣﺎن ﺳﻮﯾﭻ ﮐﺮدن ﻓﺮاﯾﻨﺪ ﻫﺎ و ﻧﺦ ﻫﺎ ﺷﺪﻧﺪ ﮐﻪ ﺣﺪود ﻧﯿﻤﯽ از زﻣﺎن ﭘﺮدازش را ﺑﻪ ﻫﺪر ﻣﯽ داد. ﺑﺮاي ﺟﺒﺮان ﺣﺎﻓﻈﻪ ﮐﺶ را ﮔﺴﺘﺮش دادﻧﺪ اﻣﺎ ﺑﻪ دﻟﯿﻞ ﮔﺮان ﺑﻮدﻧﺶ ﺑﺎز دﭼﺎر ﻣﺤﺪودﯾﺖ ﺑﻮدﻧﺪ. ﺑﻨﺎﺑﺮاﯾﻦ ﭘﺮدازﻧﺪه ﻫﺎﯾﯽ ﺗﻮﻟﯿﺪ ﮐﺮدﻧﺪ ﮐﻪ ﺑﺘﻮاﻧﺪ ﭘﺮازش ﻣﻮازي را (در اﺑﺘﺪا) در دو ﻫﺴﺘﻪ ﺑﻪ اﺟﺮا ﺑﺮﺳﺎﻧﻨﺪ.
ﻧﺎم اﯾﻦ ﻫﺴﺘﻪ ﻫﺎ ﻫﺴﺘﻪ ﻫﺎی ﺳﺨﺖ اﻓﺰاری ﯾﺎ ﻓﯿﺰﯾﮑﯽ ﮔﺬاﺷﺘﻨﺪ. اﻧﺪك زﻣﺎﻧﯽ ﺑﻌﺪ ﻓﻨﺎوری ای ﺑﺮای رﺳﯿﺪن ﺑﻪ ﭘﺮدازش ﻣﻮازی اﻣﺎ در ﺳﻄﺢ ﻣﺤﺪود Hyper ﺗﺮی و ارزان ﺗﺮ ﺑﺎ ﻧﺎم اﺑﺮ ﻧﺨﯽ ﯾﺎ Hyper-Threading اراﺋﻪ ﮐﺮدﻧﺪ و ﻧﺎم آن را ﻫﺴﺘﻪ ﻫﺎی ﻣﻨﻄﻘﯽ ﯾﺎ ﻧﺦ ﻫﺎی ﺳﺨﺖ اﻓﺰاری ﮔﺬاﺷﺘﻨﺪ. ﺣﺎل ﻧﻮﺑﺖ ﺑﺮﻧﺎﻣﻪ ﻧﻮﯾﺴﺎن ﺑﻮد ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﻫﺎی ﺑﺮاي اﺳﺘﻔﺎده از اﯾﻦ ﻓﻨﺎوری ﻫﺎی ﻧﻮﯾﻦ ﺑﻨﻮﯾﺴﻨﺪ. ﺑﺮﻧﺎﻣﻪ ﻧﻮﯾﺴﯽ ﻣﻮازی ﻋﻨﻮاﻧﯽ اﺳﺖ، ﮐﻪ ﻣﻮﺿﻮﻋﯽ ﮔﺴﺘﺮده در دﻧﯿﺎي ﻧﺮم اﻓﺰار اﯾﺠﺎد ﮐﺮده است.
روش موازی سازی(Parallelism(
ﺳﺎده ﺗﺮﯾﻦ ﺷﯿﻮه ﻣﻮازي ﺳﺎزي در ﻗﺎﻟﺐ Task ها ﺻﻮرت ﻣﯽ ﮔﯿﺮد، در ﻫﺮ Task ﺗﺎﺑﻊ ﯾﺎ ﻗﻄﻌﻪ ﮐﺪي ﻧﻮﺷﺘﻪ ﻣﯽ ﺷﻮد و ﺳﭙﺲ ﺑﻮﺳﯿﻠﻪ Delegate اي ﮐﻪ ﮐﺎر ﻣﺪﯾﺮﯾﺖ Task ﻫﺎ را ﺑﺮ ﻋﻬﺪه دارد اﯾﻦ Task ﻫﺎ ﺑﺼﻮرت ﻣﻮازی ﺑﺴﺘﻪ ﺑﻪ ﻫﺴﺘﻪ ﻫﺎی ﻣﻨﻄﻘﯽ در دﺳﺘﺮس اﺟﺮا ﻣﯽ ﺷﻮﻧﺪ. روش ﻫﺎی ﺑﺴﯿﺎری ﺑﺮاي ﻣﻮازی ﺳﺎزی وﺟﻮد دارد ﻣﺎﻧﻨﺪ اﺳﺘﻔﺎده از ﮐﻼس Parallel.For یا Parallel.ForEach ﮐﻪ در ﺟﺎي ﺧﻮد ﮐﺎرﺑﺮد ﻫﺎی ﻣﺨﺘﺺ ﺑﻪ ﺧﻮدﺷﺎن را دارﻧﺪ. همیشه الگورﯾﺘﻢ ﻫﺎی ﺗﺮﺗﯿﺒﯽ را ﻧﻤﯽ ﺗﻮان ﺑﻪ الگورﯾﺘﻤﯽ ﻣﻮازي ﺗﺒﺪﯾﻞ ﮐﺮد ﭼﺮا ﮐﻪ ﮐﺪ ﻫﺎی ﺗﺮﺗﯿﺒﯽ ای ﻫﺴﺘﻨﺪ ﮐﻪ اﺟﺮاي ﮐﺪ ﻫﺎي دﯾﮕﺮ ﻧﯿﺎز ﺑﻪ ﺗﮑﻤﯿﻞ ﺷﺪن آن ﻫﺎ دارد. ﺑﺴﺘﻪ ﺑﻪ الگورﯾﺘﻢ درﺻﺪی از آن را ﻣﯽ ﺗﻮان ﻣﻮازی ﮐﺮد. ﻗﺒﻞ از ﻣﻮازی ﺳﺎزی ﺑﺎﯾﺪ ﻣﻮازی ﺳﺎزی را در ذﻫﻨﺘﺎن ﻃﺮاﺣﯽ ﮐﻨﯿﺪ.
Parallel Programming یا برنامه نویسی موازی یعنی تقسیم یک مسئله به مسائل کوچکتر و سپردن آن ها به واحد های جداگانه برای پردازش کردن.این مسائل کوچک به صورت همزمان شروع به اجرا می کنند. Parallel Programming وظیفه یا Task را به اجزا مختلفی تقسیم می کند.
فرم های مختلفی از Parallel وجود دارد .مانند bit-level ، instruction-level، data ، taskدر این آموزش راجع به Data Parallelism و Task Parallelism بحث خواهیم کرد.
تصور کنید هسته CPUمتشکل از چندین ریزپردازنده است که همه این ها به حافظه اصلی دسترسی دارند.هر کدام از این ریزپردازنده ها قسمتی از مسئله را حل می کنند.
Data Parallelism
این مورد بر روی توزیع دیتا در نقاط مختلف تمرکز می کند.یعنی داده را به بخش های مختلفی می شکند.هر کدام از این بخش ها به Thread جداگانه ای برای پردازش داده می شود.
Task Parallelism
این مفهوم وظایف یا Taskها را به بخش هایی شکسته و هر کدام را به یک Thread جهت پردازش می دهد.
در پروژه ای که به صورت ضمیمه این مقاله می باشد (در پروژه DataParallisem)به سه صورت مختلف وظیفه یا Task تعریف شده است.
۱-به صورت Function
۲- به صورت Delegate–
۳-به صورت لامبدا
کد این قسمت به صورت زیر می باشد
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TPL_part_1_creating_simple_tasks
{
class Program
{
static void Main(string[] args)
{
//Action delegate
Task task1 = new Task(new Action(HelloConsole));
//anonymous function
Task task2 = new Task(delegate
{
HelloConsole();
});
//lambda expression
Task task3 = new Task(() = > HelloConsole());
task1.Start();
task2.Start();
task3.Start();
Console.WriteLine("Main method complete. Press any key to finish.");
Console.ReadKey();
}
static void HelloConsole()
{
Console.WriteLine("Hello Task");
}
}
}
بعد از اجرا، هر کدام از Task ها اجرا شده البته به صورت همزمان و کارهای محوله به آنها را انجام میدهند.
در این آموزش بر روی مفهوم Data Parallelism تمرکز خواهیم کرد.توسط Data Parallelism عملیات یکسانی بر روی المانهای یک مجموعه یا آرایه به صورت همزمان انجام خواهد شد. که در فضای نام System.Threading.Tasks.Parallel قرار دارد. روش اصلی برای انجام Data Parallelismنوشتن یک تابع است که یک حلقه ساده بدون Thread دارد .
public static void DataOperationWithForeachLoop()
{
var mySource = Enumerable.Range(0, 1000).ToList();
foreach (var item in mySource)
{
Console.WriteLine("Square root of {0} is {1}", item, item * item);
}
}
خروجی برنامه را در زیر می بینید
عملیات Data Parallelism را می توان با یک حلقه foreach موازی هم انجام داد.
public static void DataOperationWithDataParallelism()
{
var mySource = Enumerable.Range(0, 1000).ToList();
Parallel.ForEach(mySource, values = > CalculateMyOperation(values));
}
public static void CalculateMyOperation(int values)
{
Console.WriteLine("Square root of {0} is {1}", values, values * values);
}
بعد از اجرا شکل زیر را خواهید دید.
در این کد در داخل حلقه Foreach یک تابع Delegate قرار دادیم در این تابع به ازای هر تکرار حلقه بر روی مجموعه تابعی که درون Delegate فراخوانی کرده ایم اجرا خواهد شد.
Data Parallelism توسط PLINQ
PLINQ به معنای Parallel LINQ است .این نسخه از لینک جهت پیاده سازی لینک بر روی پردازنده های چند هسته ای نوشته شده است.
توسط لینک می توان اطلاعات را از چندین منبع بازیابی کرد.و در نهایت این نتایج با هم ترکیب می شوند تا نتیجه نهایی Query به دست آید.اما اگر از PLINQاستفاده کنیم این دستورات به جای اینکه پشت سر هم اجرا شوند به صورت موازی اجرا می شوند.
برای این که از PLINQ استفاده کرد فقط کافی است که در انتهای عبارت لینک از AsParallel استفاده کنیم.به کد زیر توجه کنید
public static void DataOperationByPLINQ()
{
long mySum = Enumerable.Range(1, 10000).AsParallel().Sum();
Console.WriteLine("Total: {0}", mySum);
}
بعد از اجرا شکل زیر را خواهید دید
برای به دست آوردن اعداد فرد در این مجموعه توسط Plinq از کد زیر استفاده می کنیم
public static void ShowEvenNumbersByPLINQ()
{
var numers = Enumerable.Range(1, 10000);
var evenNums = from number in numers.AsParallel()
where number % 2 == 0
select number;
Console.WriteLine("Even Counts :{0} :", evenNums.Count());
}
پس از اجرا شکل زیر را خواهید دید
توسط متد Parallel.Invoke() می توانید چندین متد را مانند شکل زیر به صورت همزمان اجرا کنید.
ماکزیمم تعداد پردازش های موازی را مشخص می کند در کد زیر و در داخل Foreach در پارامتر دوم ماکزیمم تعداد پردازش های موازی مشخص شده اشت.
loopState.Break()
توسط این کد به Thread هایی که پردازش آنها طول کشیده اجازه می دهیم که بعدا Break شوند.به کد زیر توجه کنید.
var mySource = Enumerable.Range(0, 1000).ToList();
int data = 0;
Parallel.ForEach(
mySource,
(i, state) = >
{
data += i;
if (data > 100)
{
state.Break();
Console.WriteLine("Break called iteration {0}. data = {1} ", i, data);
}
});
Console.WriteLine("Break called data = {0} ", data);
Console.ReadKey();
https://behsanandish.com/wp-content/uploads/images-5-1.jpg344304م. دلیریhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngم. دلیری2020-03-29 10:00:502019-11-30 10:14:08آموزش Parallel Programming در سی شارپ
تکنولوژی EFFIO-V و EFFIO-A که توسط شرکت سونی به بازار عرضه شده اند با قابلیت و عملکردهای بسیار توسعه یافته به عنوان نسل سوم سری EFFIO در نظر گرفته می شود.
این دو محصول جدید میتوانند با سنسور تصویر ۹۶۰H ترکیب شده تا بتوانند کیفیت تصویری بالاتر از ۷۰۰ تی وی لاین را ایجاد نماید. همچنین این آی سی ها بهبود یافتند تا قابلیت هایی از قبیل کاهش نویز سه بعدی (۳D-NR)و قرار گرفتن در معرض مادون قرمز و طیف گسترده ای پویا (WDR) و یا مه زدا DEFOG)) و نمایش تصاویر در شرایط مختلف مانند نور کم ، نور مادون قرمز و نور زیاد که کاملا توسعه یافته در عملکرد خود داشته باشند. علاوه بر این این آی سی ها برای اولین بار قابلیت تشخیص اتوماتیک به صورت صنعتی و انتخاب صحنه را دارا هستند.
EFFIO-V و EFFIO-A ، اصلاح شده تا پردازشگر سیگنال را با وضوح بالا انجام دهد و رزولوشن بالای ۷۰۰تی وی لاین را ایجاد کند که این رزولوشن بالاتر از رزولوشن سری های EFFIOموجود ۶۵۰ تی وی لاین است .
قابلیت کاهش نویز سه بعدی
این تکنولوژی تاری موضوعات در حال حرکت ، روشنی و سیگنال به نویز تصاویری که حتی در محیط های کم نور هستند را کاهش می دهد. همچنین به طور موثر از شرایط زمانی که انعکاس نور مادون قرمز بیش از حد شده باشد و یا جزئیات نفر پنهان شده باشد و یا زمانی که در اطراف لبه ای بیرونی تصاویر و یا گوشه های تاریک شده (سایه) باشد جلوگیری می کند. بنابراین قابلیت این تکنولوژی بهبود بخشیدن تصاویر در محیط های کم نور می باشد.
این محصولات عملکرد مناسب در هر شرایطی برای تنظیم تصاویر را دارا هستند . بعضی از نصب ها زمان زیادی برای تنظیم چند دوربین با ویژگی های مختلف برای کیفیت بهتر را از ما می گیرند . اما EFFIO-V و EFFIO-A قابلیت تشخیص اتوماتیک صحنه را دارند و فقط نیاز به یک عملکرد برای ۴۰الگو در صحنه های تصاویر را برای تصویری ایده آل مثل محدوده دینامیکی ، درجه حرارت ، رنگ را دارد. قابلیت انتخاب صحنه از پیش تنظیم شده برای صحنه های عمومی از جمله دوربین های محیط داخل و محیط خارج نصب شده یا نظارت ترافیکی و یا نور پس زمینه تنظیمات اتوماتیک آنها بر پایه تنظیمات برای کیفیت تصویر ایده آل می باشد .
این قابلیت تنظیمات اتوماتیک برای آسان تر شدن نصب و راه اندازی تصاویر با کیفیت است .
از فضای رنگی مختلف استفاده می کنیم چون این امکان در آن فضای رنگی به ما داده میشه تا بتوینم رنگ دلخواه مان را به راحتی از محدوده دیگر رنگ ها جدا کنیم .فرض کنید که شما قصد دارید رنگ سبز را در تصویر فیلتر نمایید این بازه شامل طیفی می باشد که یک سمت آن سبز تیره و در سمت دیگر آن سبز روشن می باشد برای جدا کردن آن در فضای رنگی RGB این امکان وجود ندارد که شما بتوان به صورت خطی یعنی هر کانال با یک شرط بازه رنگ دلخواه را انتخاب نمائید پس به خاطر چنین مشکلاتی تصویر را به فضای رنگی HSV انتقال می دهیم که این فضا از اجزای Hue (رنگدانه) ،Saturation(اشباع) و Value(روشنایی) تشکیل شده.برای تفکیک رنگ سبز در این فضای رنگی کافیست محدوده Hue خود که مربوط به رنگ مورد نظر را انتخاب کرده و سپس کل محدوه اشباع و در نهایت انتخاب محدوده دلخواه برای روشنایی پس در این فضای رنگی به راحتی تونستید رنگ دلخواه خودتون را انتخاب کنید.
تبدیل فضای رنگی در opencv
در کتابخانه Opencv می تونیم از تابع cvtColor استفاده کنیم.
مثال:
/*------------------------------------------------------------------------------------------*\
This file contains material supporting chapter 3 of the cookbook:
Computer Vision Programming using the OpenCV Library
Second Edition
by Robert Laganiere, Packt Publishing, 2013.
This program is free software; permission is hereby granted to use, copy, modify,
and distribute this source code, or portions thereof, for any purpose, without fee,
subject to the restriction that the copyright notice may not be removed
or altered from any source or altered source distribution.
The software is released on an as-is basis and without any warranties of any kind.
In particular, the software is not guaranteed to be fault-tolerant or free from failure.
The author disclaims all warranties with regard to this software, any use,
and any consequent failure, is purely the responsibility of the user.
Copyright (C) 2013 Robert Laganiere, www.laganiere.name
\*------------------------------------------------------------------------------------------*/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
void detectHScolor(const cv::Mat& image, // input image
double minHue, double maxHue, // Hue interval
double minSat, double maxSat, // saturation interval
cv::Mat& mask) { // output mask
// convert into HSV space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// split the 3 channels into 3 images
std::vector<cv::Mat> channels;
cv::split(hsv, channels);
// channels[0] is the Hue
// channels[1] is the Saturation
// channels[2] is the Value
// Hue masking
cv::Mat mask1; // under maxHue
cv::threshold(channels[0], mask1, maxHue, 255, cv::THRESH_BINARY_INV);
cv::Mat mask2; // over minHue
cv::threshold(channels[0], mask2, minHue, 255, cv::THRESH_BINARY);
cv::Mat hueMask; // hue mask
if (minHue < maxHue)
hueMask = mask1 & mask2;
else // if interval crosses the zero-degree axis
hueMask = mask1 | mask2;
// Saturation masking
// under maxSat
cv::threshold(channels[1], mask1, maxSat, 255, cv::THRESH_BINARY_INV);
// over minSat
cv::threshold(channels[1], mask2, minSat, 255, cv::THRESH_BINARY);
cv::Mat satMask; // saturation mask
satMask = mask1 & mask2;
// combined mask
mask = hueMask&satMask;
}
int main()
{
// read the image
cv::Mat image= cv::imread("boldt.jpg");
if (!image.data)
return 0;
// show original image
cv::namedWindow("Original image");
cv::imshow("Original image",image);
// convert into HSV space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// split the 3 channels into 3 images
std::vector<cv::Mat> channels;
cv::split(hsv,channels);
// channels[0] is the Hue
// channels[1] is the Saturation
// channels[2] is the Value
// display value
cv::namedWindow("Value");
cv::imshow("Value",channels[2]);
// display saturation
cv::namedWindow("Saturation");
cv::imshow("Saturation",channels[1]);
// display hue
cv::namedWindow("Hue");
cv::imshow("Hue",channels[0]);
// image with fixed value
cv::Mat newImage;
cv::Mat tmp(channels[2].clone());
// Value channel will be 255 for all pixels
channels[2]= 255;
// merge back the channels
cv::merge(channels,hsv);
// re-convert to BGR
cv::cvtColor(hsv,newImage,CV_HSV2BGR);
cv::namedWindow("Fixed Value Image");
cv::imshow("Fixed Value Image",newImage);
// image with fixed saturation
channels[1]= 255;
channels[2]= tmp;
cv::merge(channels,hsv);
cv::cvtColor(hsv,newImage,CV_HSV2BGR);
cv::namedWindow("Fixed saturation");
cv::imshow("Fixed saturation",newImage);
// image with fixed value and fixed saturation
channels[1]= 255;
channels[2]= 255;
cv::merge(channels,hsv);
cv::cvtColor(hsv,newImage,CV_HSV2BGR);
cv::namedWindow("Fixed saturation/value");
cv::imshow("Fixed saturation/value",newImage);
// Testing skin detection
// read the image
image= cv::imread("girl.jpg");
if (!image.data)
return 0;
// show original image
cv::namedWindow("Original image");
cv::imshow("Original image",image);
// detect skin tone
cv::Mat mask;
detectHScolor(image,
۱۶۰, ۱۰, // hue from 320 degrees to 20 degrees
۲۵, ۱۶۶, // saturation from ~0.1 to 0.65
mask);
// show masked image
cv::Mat detected(image.size(), CV_8UC3, cv::Scalar(0, 0, 0));
image.copyTo(detected, mask);
cv::imshow("Detection result",detected);
// A test comparing luminance and brightness
// create linear intensity image
cv::Mat linear(100,256,CV_8U);
for (int i=0; i<256; i++) {
linear.col(i)= i;
}
// create a Lab image
linear.copyTo(channels[0]);
cv::Mat constante(100,256,CV_8U,cv::Scalar(128));
constante.copyTo(channels[1]);
constante.copyTo(channels[2]);
cv::merge(channels,image);
// convert back to BGR
cv::Mat brightness;
cv::cvtColor(image,brightness, CV_Lab2BGR);
cv::split(brightness, channels);
// create combined image
cv::Mat combined(200,256, CV_8U);
cv::Mat half1(combined,cv::Rect(0,0,256,100));
linear.copyTo(half1);
cv::Mat half2(combined,cv::Rect(0,100,256,100));
channels[0].copyTo(half2);
cv::namedWindow("Luminance vs Brightness");
cv::imshow("Luminance vs Brightness",combined);
cv::waitKey();
}
https://behsanandish.com/wp-content/uploads/HSV_cone.jpg450600محمد مهدی ابراهیمیhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngمحمد مهدی ابراهیمی2020-03-28 10:00:242019-11-30 10:13:53تبدیل فضای رنگی به یک دیگر
آخرین مرحله، پیدا کردن لبه های ضعیف که موازی با لبه های قوی هستند و از بین بردن آنهاست. این عمل، با بررسی پیکسل های عمود بر یک پیکسل لبه خاص و حذف لبه های غیر حداکثرانجام شده است. کد مورد استفاده بسیار مشابه کد ردیابی لبه است.
</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;
}
}
https://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.png00توسعه دهندهhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngتوسعه دهنده2020-03-20 10:00:412019-11-30 10:08:25الگوریتم Canny در سی پلاس پلاس قسمت 4
گام بعدی در واقع این است که در امتداد لبه ها بر اساس نقاط قوت و جهت های لبه که قبلا محاسبه شده است ردیابی شود. هر پیکسل از طریق استفاده از دو تودرتو برای حلقه ها چرخه می زند. اگر پیکسل فعلی دارای قدرت شیب بیشتر از مقدار upperThreshold تعریف شده باشد، یک سوئیچ اجرا می شود. این سوئیچ توسط جهت لبه پیکسل فعلی تعیین می شود. این ردیف و ستون، پیکسل ممکن بعدی را در این جهت ذخیره می کند و سپس جهت لبه و استحکام شیب آن پیکسل را آزمایش می کند. اگر آن همان جهت لبه و قدرت گرادیان بزرگتر از lowerThreshold را دارد، آن پیکسل به سفید و پیکسل بعدی در امتداد آن لبه آزمایش می شود. به این ترتیب هر لبه قابل توجه تیز تشخیص داده شده و به سفید تنظیم می شود در حالیکه تمام پیکسل های دیگر به سیاه تنظیم می شود.
#include "stdafx.h"
#include "tripod.h"
#include "tripodDlg.h"
#include "LVServerDefs.h"
#include "math.h"
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTripodDlg dialog
CTripodDlg::CTripodDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTripodDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CTripodDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//////////////// Set destination BMP to NULL first
m_destinationBitmapInfoHeader = NULL;
}
////////////////////// Additional generic functions
static unsigned PixelBytes(int w, int bpp)
{
return (w * bpp + 7) / 8;
}
static unsigned DibRowSize(int w, int bpp)
{
return (w * bpp + 31) / 32 * 4;
}
static unsigned DibRowSize(LPBITMAPINFOHEADER pbi)
{
return DibRowSize(pbi->biWidth, pbi->biBitCount);
}
static unsigned DibRowPadding(int w, int bpp)
{
return DibRowSize(w, bpp) - PixelBytes(w, bpp);
}
static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
return DibRowPadding(pbi->biWidth, pbi->biBitCount);
}
static unsigned DibImageSize(int w, int h, int bpp)
{
return h * DibRowSize(w, bpp);
}
static size_t DibSize(int w, int h, int bpp)
{
return sizeof (BITMAPINFOHEADER) + DibImageSize(w, h, bpp);
}
/////////////////////// end of generic functions
void CTripodDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTripodDlg)
DDX_Control(pDX, IDC_PROCESSEDVIEW, m_cVideoProcessedView);
DDX_Control(pDX, IDC_UNPROCESSEDVIEW, m_cVideoUnprocessedView);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CTripodDlg, CDialog)
//{{AFX_MSG_MAP(CTripodDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDEXIT, OnExit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTripodDlg message handlers
BOOL CTripodDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
// For Unprocessed view videoportal (top one)
char sRegUnprocessedView[] = "HKEY_LOCAL_MACHINE\\Software\\UnprocessedView";
m_cVideoUnprocessedView.PrepareControl("UnprocessedView", sRegUnprocessedView, 0 );
m_cVideoUnprocessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
m_cVideoUnprocessedView.ConnectCamera2();
m_cVideoUnprocessedView.SetEnablePreview(TRUE);
// For binary view videoportal (bottom one)
char sRegProcessedView[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedView";
m_cVideoProcessedView.PrepareControl("ProcessedView", sRegProcessedView, 0 );
m_cVideoProcessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
m_cVideoProcessedView.ConnectCamera2();
m_cVideoProcessedView.SetEnablePreview(TRUE);
// Initialize the size of binary videoportal
m_cVideoProcessedView.SetPreviewMaxHeight(240);
m_cVideoProcessedView.SetPreviewMaxWidth(320);
// Uncomment if you wish to fix the live videoportal's size
// m_cVideoUnprocessedView.SetPreviewMaxHeight(240);
// m_cVideoUnprocessedView.SetPreviewMaxWidth(320);
// Find the screen coodinates of the binary videoportal
m_cVideoProcessedView.GetWindowRect(m_rectForProcessedView);
ScreenToClient(m_rectForProcessedView);
allocateDib(CSize(320, 240));
// Start grabbing frame data for Procssed videoportal (bottom one)
m_cVideoProcessedView.StartVideoHook(0);
return TRUE; // return TRUE unless you set the focus to a control
}
void CTripodDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CTripodDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CTripodDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CTripodDlg::OnExit()
{
// TODO: Add your control notification handler code here
// Kill live view videoportal (top one)
m_cVideoUnprocessedView.StopVideoHook(0);
m_cVideoUnprocessedView.DisconnectCamera();
// Kill binary view videoportal (bottom one)
m_cVideoProcessedView.StopVideoHook(0);
m_cVideoProcessedView.DisconnectCamera();
// Kill program
DestroyWindow();
}
BEGIN_EVENTSINK_MAP(CTripodDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTripodDlg)
ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEW, 1 /* PortalNotification */, OnPortalNotificationProcessedview, VTS_I4 VTS_I4 VTS_I4 VTS_I4)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
void CTripodDlg::OnPortalNotificationProcessedview(long lMsg, long lParam1, long lParam2, long lParam3)
{
// TODO: Add your control notification handler code here
// This function is called at the camera's frame rate
#define NOTIFICATIONMSG_VIDEOHOOK ۱۰
// Declare some useful variables
// QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too
LPBITMAPINFOHEADER lpBitmapInfoHeader; // Frame's info header contains info like width and height
LPBYTE lpBitmapPixelData; // This pointer-to-long will point to the start of the frame's pixel data
unsigned long lTimeStamp; // Time when frame was grabbed
switch(lMsg) {
case NOTIFICATIONMSG_VIDEOHOOK:
{
lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1;
lpBitmapPixelData = (LPBYTE) lParam2;
lTimeStamp = (unsigned long) lParam3;
grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData);
doMyImageProcessing(lpBitmapInfoHeader); // Place where you'd add your image processing code
displayMyResults(lpBitmapInfoHeader);
}
break;
default:
break;
}
}
void CTripodDlg::allocateDib(CSize sz)
{
// Purpose: allocate information for a device independent bitmap (DIB)
// Called from OnInitVideo
if(m_destinationBitmapInfoHeader) {
free(m_destinationBitmapInfoHeader);
m_destinationBitmapInfoHeader = NULL;
}
if(sz.cx | sz.cy) {
m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
ASSERT(m_destinationBitmapInfoHeader);
m_destinationBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
m_destinationBitmapInfoHeader->biWidth = sz.cx;
m_destinationBitmapInfoHeader->biHeight = sz.cy;
m_destinationBitmapInfoHeader->biPlanes = 1;
m_destinationBitmapInfoHeader->biBitCount = 24;
m_destinationBitmapInfoHeader->biCompression = 0;
m_destinationBitmapInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
m_destinationBitmapInfoHeader->biXPelsPerMeter = 0;
m_destinationBitmapInfoHeader->biYPelsPerMeter = 0;
m_destinationBitmapInfoHeader->biClrImportant = 0;
m_destinationBitmapInfoHeader->biClrUsed = 0;
}
}
void CTripodDlg::displayMyResults(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
// displayMyResults: Displays results of doMyImageProcessing() in the videoport
// Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size
CDC *pDC; // Device context to display bitmap data
pDC = GetDC();
int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR);
StretchDIBits(
pDC->GetSafeHdc(),
m_rectForProcessedView.left, // videoportal left-most coordinate
m_rectForProcessedView.top, // videoportal top-most coordinate
m_rectForProcessedView.Width(), // videoportal width
m_rectForProcessedView.Height(), // videoportal height
۰, // Row position to display bitmap in videoportal
۰, // Col position to display bitmap in videoportal
lpThisBitmapInfoHeader->biWidth, // m_destinationBmp's number of columns
lpThisBitmapInfoHeader->biHeight, // m_destinationBmp's number of rows
m_destinationBmp, // The bitmap to display; use the one resulting from doMyImageProcessing
(BITMAPINFO*)m_destinationBitmapInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc
DIB_RGB_COLORS, // Use default 24-bit color table
SRCCOPY // Just display
);
SetStretchBltMode(pDC->GetSafeHdc(),nOldMode);
ReleaseDC(pDC);
// Note: 04/24/02 - Added the following:
// Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed
// Recall OnPortalNotificationProcessedview, which gets called everytime
// a frame of data arrives, performs 3 steps:
// (۱) grayScaleTheFrameData - which mallocs m_destinationBmp
// (۲) doMyImageProcesing
// (۳) displayMyResults - which we're in now
// Since we're finished with the memory we malloc'ed for m_destinationBmp
// we should free it:
free(m_destinationBmp);
// End of adds
}
void CTripodDlg::grayScaleTheFrameData(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData)
{
// grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview
// Task: Read current frame pixel data and computes a grayscale version
unsigned int W, H; // Width and Height of current frame [pixels]
BYTE *sourceBmp; // Pointer to current frame of data
unsigned int row, col;
unsigned long i;
BYTE grayValue;
BYTE redValue;
BYTE greenValue;
BYTE blueValue;
W = lpThisBitmapInfoHeader->biWidth; // biWidth: number of columns
H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
// Store pixel data in row-column vector format
// Recall that each pixel requires 3 bytes (red, blue and green bytes)
// m_destinationBmp is a protected member and declared in binarizeDlg.h
m_destinationBmp = (BYTE*)malloc(H*3*W*sizeof(BYTE));
// Point to the current frame's pixel data
sourceBmp = lpThisBitmapPixelData;
for (row = 0; row < H; row++) {
for (col = 0; col < W; col++) {
// Recall each pixel is composed of 3 bytes
i = (unsigned long)(row*3*W + 3*col);
// The source pixel has a blue, green andred value:
blueValue = *(sourceBmp + i);
greenValue = *(sourceBmp + i + 1);
redValue = *(sourceBmp + i + 2);
// A standard equation for computing a grayscale value based on RGB values
grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue);
// The destination BMP will be a grayscale version of the source BMP
*(m_destinationBmp + i) = grayValue;
*(m_destinationBmp + i + 1) = grayValue;
*(m_destinationBmp + i + 2) = grayValue;
}
}
}
void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
// doMyImageProcessing: This is where you'd write your own image processing code
// Task: Read a pixel's grayscale value and process accordingly
unsigned int W, H; // Width and Height of current frame [pixels]
unsigned int row, col; // Pixel's row and col positions
unsigned long i; // Dummy variable for row-column vector
int upperThreshold = 60; // Gradient strength nessicary to start edge
int lowerThreshold = 30; // Minimum gradient strength to continue edge
unsigned long iOffset; // Variable to offset row-column vector during sobel mask
int rowOffset; // Row offset from the current pixel
int colOffset; // Col offset from the current pixel
int rowTotal = 0; // Row position of offset pixel
int colTotal = 0; // Col position of offset pixel
int Gx; // Sum of Sobel mask products values in the x direction
int Gy; // Sum of Sobel mask products values in the y direction
float thisAngle; // Gradient direction based on Gx and Gy
int newAngle; // Approximation of the gradient direction
bool edgeEnd; // Stores whether or not the edge is at the edge of the possible image
int GxMask[3][3]; // Sobel mask in the x direction
int GyMask[3][3]; // Sobel mask in the y direction
int newPixel; // Sum pixel values for gaussian
int gaussianMask[5][5]; // Gaussian mask
W = lpThisBitmapInfoHeader->biWidth; // biWidth: number of columns
H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
for (row = 0; row < H; row++) {
for (col = 0; col < W; col++) {
edgeDir[row][col] = 0;
}
}
/* Declare Sobel masks */
GxMask[0][0] = -1; GxMask[0][1] = 0; GxMask[0][2] = 1;
GxMask[1][0] = -2; GxMask[1][1] = 0; GxMask[1][2] = 2;
GxMask[2][0] = -1; GxMask[2][1] = 0; GxMask[2][2] = 1;
GyMask[0][0] = 1; GyMask[0][1] = 2; GyMask[0][2] = 1;
GyMask[1][0] = 0; GyMask[1][1] = 0; GyMask[1][2] = 0;
GyMask[2][0] = -1; GyMask[2][1] = -2; GyMask[2][2] = -1;
/* Declare Gaussian mask */
gaussianMask[0][0] = 2; gaussianMask[0][1] = 4; gaussianMask[0][2] = 5; gaussianMask[0][3] = 4; gaussianMask[0][4] = 2;
gaussianMask[1][0] = 4; gaussianMask[1][1] = 9; gaussianMask[1][2] = 12; gaussianMask[1][3] = 9; gaussianMask[1][4] = 4;
gaussianMask[2][0] = 5; gaussianMask[2][1] = 12; gaussianMask[2][2] = 15; gaussianMask[2][3] = 12; gaussianMask[2][4] = 2;
gaussianMask[3][0] = 4; gaussianMask[3][1] = 9; gaussianMask[3][2] = 12; gaussianMask[3][3] = 9; gaussianMask[3][4] = 4;
gaussianMask[4][0] = 2; gaussianMask[4][1] = 4; gaussianMask[4][2] = 5; gaussianMask[4][3] = 4; gaussianMask[4][4] = 2;
/* Gaussian Blur */
for (row = 2; row < H-2; row++) {
for (col = 2; col < W-2; col++) {
newPixel = 0;
for (rowOffset=-2; rowOffset<=2; rowOffset++) {
for (colOffset=-2; colOffset<=2; colOffset++) {
rowTotal = row + rowOffset;
colTotal = col + colOffset;
iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
newPixel += (*(m_destinationBmp + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset];
}
}
i = (unsigned long)(row*3*W + col*3);
*(m_destinationBmp + i) = newPixel / 159;
}
}
/* Determine edge directions and gradient strengths */
for (row = 1; row < H-1; row++) {
for (col = 1; col < W-1; col++) {
i = (unsigned long)(row*3*W + 3*col);
Gx = 0;
Gy = 0;
/* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */
for (rowOffset=-1; rowOffset<=1; rowOffset++) {
for (colOffset=-1; colOffset<=1; colOffset++) {
rowTotal = row + rowOffset;
colTotal = col + colOffset;
iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
Gx = Gx + (*(m_destinationBmp + iOffset) * GxMask[rowOffset + 1][colOffset + 1]);
Gy = Gy + (*(m_destinationBmp + iOffset) * GyMask[rowOffset + 1][colOffset + 1]);
}
}
gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0)); // Calculate gradient strength
thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0; // Calculate actual direction of edge
/* Convert actual edge direction to approximate value */
if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) )
newAngle = 0;
if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) )
newAngle = 45;
if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) )
newAngle = 90;
if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) )
newAngle = 135;
edgeDir[row][col] = newAngle; // Store the approximate edge direction of each pixel in one array
}
}
/* Trace along all the edges in the image */
for (row = 1; row < H - 1; row++) {
for (col = 1; col < W - 1; col++) {
edgeEnd = false;
if (gradient[row][col] > upperThreshold) { // Check to see if current pixel has a high enough gradient strength to be part of an edge
/* Switch based on current pixel's edge direction */
switch (edgeDir[row][col]){
case 0:
findEdge(0, 1, row, col, 0, lowerThreshold);
break;
case 45:
findEdge(1, 1, row, col, 45, lowerThreshold);
break;
case 90:
findEdge(1, 0, row, col, 90, lowerThreshold);
break;
case 135:
findEdge(1, -1, row, col, 135, lowerThreshold);
break;
default :
i = (unsigned long)(row*3*W + 3*col);
*(m_destinationBmp + i) =
*(m_destinationBmp + i + 1) =
*(m_destinationBmp + i + 2) = 0;
break;
}
}
else {
i = (unsigned long)(row*3*W + 3*col);
*(m_destinationBmp + i) =
*(m_destinationBmp + i + 1) =
*(m_destinationBmp + i + 2) = 0;
}
}
}
/* Suppress any pixels not changed by the edge tracing */
for (row = 0; row < H; row++) {
for (col = 0; col < W; col++) {
// Recall each pixel is composed of 3 bytes
i = (unsigned long)(row*3*W + 3*col);
// If a pixel's grayValue is not black or white make it black
if( ((*(m_destinationBmp + i) != 255) && (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) && (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) && (*(m_destinationBmp + i + 2) != 0)) )
*(m_destinationBmp + i) =
*(m_destinationBmp + i + 1) =
*(m_destinationBmp + i + 2) = 0; // Make pixel black
}
}
/* Non-maximum Suppression */
for (row = 1; row < H - 1; row++) {
for (col = 1; col < W - 1; col++) {
i = (unsigned long)(row*3*W + 3*col);
if (*(m_destinationBmp + i) == 255) { // Check to see if current pixel is an edge
/* Switch based on current pixel's edge direction */
switch (edgeDir[row][col]) {
case 0:
suppressNonMax( 1, 0, row, col, 0, lowerThreshold);
break;
case 45:
suppressNonMax( 1, -1, row, col, 45, lowerThreshold);
break;
case 90:
suppressNonMax( 0, 1, row, col, 90, lowerThreshold);
break;
case 135:
suppressNonMax( 1, 1, row, col, 135, lowerThreshold);
break;
default :
break;
}
}
}
}
}
void CTripodDlg::findEdge(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
int W = 320;
int H = 240;
int newRow;
int newCol;
unsigned long i;
bool edgeEnd = false;
/* Find the row and column values for the next possible pixel on the edge */
if (colShift < 0) {
if (col > 0)
newCol = col + colShift;
else
edgeEnd = true;
} else if (col < W - 1) {
newCol = col + colShift;
} else
edgeEnd = true; // If the next pixel would be off image, don't do the while loop
if (rowShift < 0) {
if (row > 0)
newRow = row + rowShift;
else
edgeEnd = true;
} else if (row < H - 1) {
newRow = row + rowShift;
} else
edgeEnd = true;
/* Determine edge directions and gradient strengths */
while ( (edgeDir[newRow][newCol]==dir) && !edgeEnd && (gradient[newRow][newCol] > lowerThreshold) ) {
/* Set the new pixel as white to show it is an edge */
i = (unsigned long)(newRow*3*W + 3*newCol);
*(m_destinationBmp + i) =
*(m_destinationBmp + i + 1) =
*(m_destinationBmp + i + 2) = 255;
if (colShift < 0) {
if (newCol > 0)
newCol = newCol + colShift;
else
edgeEnd = true;
} else if (newCol < W - 1) {
newCol = newCol + colShift;
} else
edgeEnd = true;
if (rowShift < 0) {
if (newRow > 0)
newRow = newRow + rowShift;
else
edgeEnd = true;
} else if (newRow < H - 1) {
newRow = newRow + rowShift;
} else
edgeEnd = true;
}
}
void CTripodDlg::suppressNonMax(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
int W = 320;
int H = 240;
int newRow = 0;
int newCol = 0;
unsigned long i;
bool edgeEnd = false;
float nonMax[320][3]; // Temporarily stores gradients and positions of pixels in parallel edges
int pixelCount = 0; // Stores the number of pixels in parallel edges
int count; // A for loop counter
int max[3]; // Maximum point in a wide edge
if (colShift < 0) {
if (col > 0)
newCol = col + colShift;
else
edgeEnd = true;
} else if (col < W - 1) {
newCol = col + colShift;
} else
edgeEnd = true; // If the next pixel would be off image, don't do the while loop
if (rowShift < 0) {
if (row > 0)
newRow = row + rowShift;
else
edgeEnd = true;
} else if (row < H - 1) {
newRow = row + rowShift;
} else
edgeEnd = true;
i = (unsigned long)(newRow*3*W + 3*newCol);
/* Find non-maximum parallel edges tracing up */
while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
if (colShift < 0) {
if (newCol > 0)
newCol = newCol + colShift;
else
edgeEnd = true;
} else if (newCol < W - 1) {
newCol = newCol + colShift;
} else
edgeEnd = true;
if (rowShift < 0) {
if (newRow > 0)
newRow = newRow + rowShift;
else
edgeEnd = true;
} else if (newRow < H - 1) {
newRow = newRow + rowShift;
} else
edgeEnd = true;
nonMax[pixelCount][0] = newRow;
nonMax[pixelCount][1] = newCol;
nonMax[pixelCount][2] = gradient[newRow][newCol];
pixelCount++;
i = (unsigned long)(newRow*3*W + 3*newCol);
}
/* Find non-maximum parallel edges tracing down */
edgeEnd = false;
colShift *= -1;
rowShift *= -1;
if (colShift < 0) {
if (col > 0)
newCol = col + colShift;
else
edgeEnd = true;
} else if (col < W - 1) {
newCol = col + colShift;
} else
edgeEnd = true;
if (rowShift < 0) {
if (row > 0)
newRow = row + rowShift;
else
edgeEnd = true;
} else if (row < H - 1) {
newRow = row + rowShift;
} else
edgeEnd = true;
i = (unsigned long)(newRow*3*W + 3*newCol);
while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
if (colShift < 0) {
if (newCol > 0)
newCol = newCol + colShift;
else
edgeEnd = true;
} else if (newCol < W - 1) {
newCol = newCol + colShift;
} else
edgeEnd = true;
if (rowShift < 0) {
if (newRow > 0)
newRow = newRow + rowShift;
else
edgeEnd = true;
} else if (newRow < H - 1) {
newRow = newRow + rowShift;
} else
edgeEnd = true;
nonMax[pixelCount][0] = newRow;
nonMax[pixelCount][1] = newCol;
nonMax[pixelCount][2] = gradient[newRow][newCol];
pixelCount++;
i = (unsigned long)(newRow*3*W + 3*newCol);
}
/* Suppress non-maximum edges */
max[0] = 0;
max[1] = 0;
max[2] = 0;
for (count = 0; count < pixelCount; count++) {
if (nonMax[count][2] > max[2]) {
max[0] = nonMax[count][0];
max[1] = nonMax[count][1];
max[2] = nonMax[count][2];
}
}
for (count = 0; count < pixelCount; count++) {
i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
*(m_destinationBmp + i) =
*(m_destinationBmp + i + 1) =
*(m_destinationBmp + i + 2) = 0;
}
}
https://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.png00توسعه دهندهhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngتوسعه دهنده2020-03-19 10:00:372019-11-30 10:07:36الگوریتم Canny در سی پلاس پلاس قسمت 3
گام بعدی استفاده از Mask های Sobel برای پیدا کردن قدرت و جهت گرادیان لبه برای هر پیکسل است. ابتدا ماسک های Sobel به محدوده پیکسل ۳×۳ پیکسل فعلی در هر دو جهت x و y اعمال می شود. سپس مجموع مقدار هر ماسک ضربدر پیکسل مربوطه به ترتیب به عنوان مقادیر Gx و Gy محاسبه می شود. ریشه دوم مربع Gx به اضافه Gy مربع برابر قدرت لبه است. Tangent معکوس Gx / Gy جهت لبه را تولید می کند. سپس جهت لبه تقریب شده است به یکی از چهار مقادیر ممکن که ایجاد می کند جهت های ممکن را که یک لبه می تواند در یک تصویر از یک شبکه پیکسل مربع باشد. این جهت لبه در edgeDir [row] [col] ذخیره می شود و قدرت گرادیان در array gradient[row] [col] ذخیره می شود.
هر زاویه لبه در ۱۱٫۲۵ درجه از یکی از زاویه های ممکن به آن مقدار تغییر می کند.
#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;
}
}
https://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.png00توسعه دهندهhttps://behsanandish.com/wp-content/uploads/logo-farsi-englisi-300x195-1.pngتوسعه دهنده2020-03-18 10:00:332019-11-30 10:07:11الگوریتم Canny در سی پلاس پلاس قسمت 2