بایگانی برچسب برای: کنترل تردد

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

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

فایل PDF – در 7 صفحه- نویسندگان : محمدجواد اکبریان میمند، سمانه فرجی کفشگری، علیرضا صادقی ماهونک، سید عبدالله حسینی شرقی، مهدی وطن خواه

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

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


8. شناسایی ضایعات خط تولید با الگوی پردازش تصویر در شرکت روغن نباتی شیراز

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

فایل PDF – در 11 صفحه- نویسندگان : سید محمدعلی خاتمی فیروزآبادی، وجیهه ظریف

شناسایی ضایعات خط تولید با الگوی پردازش تصویر در شرکت روغن نباتی شیراز

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


9. طراحی سیستم خبره بر پایه پردازش تصویر به منظور کنترل کیفیت مفتول های مسی

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

فایل PDF – در 7 صفحه- نویسندگان : محمدمهدی دهدار، مصطفی جهانگشای رضائی، مرضیه زرین بال ماسوله، حمیدرضا ایزدبخش

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

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


10. کنترل ابعادي ذرات شن و ماسه بر اساس استاندارد ملی ایران با استفاده از تکنیک پردازش تصویر

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

کلیدواژگان: دانه بندي ذرات شن و ماسه، ذرات شن و ماسه، پردازش تصویر، توصیفگر سري فوریه، شکل شناسی

فایل PDF – در 11 صفحه- نویسندگان : خلیل خلیلی، سید محمد امام

کنترل ابعادي ذرات شن و ماسه بر اساس استاندرد ملی ایران با استفاده از تکنیک پردازش تصویر

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


11. كنترل كيفيت فرآيند و فرآورده های غذايی با استفاده از سيستمهای بينايی كامپيوتری

چكيده: روشهاي معمول بازرسي، درجه بنـدي و سـورتينگ دستي محصولات كشاورزي و فرآورده هاي غذايي هزينه بـر، وقت گير و سخت هستند، بعلاوه نتيجۀ عملكرد ايـن روشـها نيز قابل تضمين نبوده و كنتـرل كيفيـت يكنواخـت و پايـدار محصولات غذايي با اين روشها امكان پذير نيست. در مقابـل سيستم هاي بينايي كامپيوتري غير مخرب، كارآمد و مقرون به صرفه بوده و نيز نتايج باثبات تر و پايدارتري ارائه مـي كننـد. بطور كلی سيـستم هــاي بينـايي كــامپيوتري بر مبنای تصويربرداري از محصول، حتي در حين عبور از خط توليد و سپس پردازش تصوير گرفته شده و آناليز آن كـار مـي كننـد. سيستم هاي بينايي كامپيوتري نه تنها انـدازه، شـكل، رنـگ و بافت اشياء را تشخيص مي دهند بلكه ويژگيهاي عددي اشياء يا صحنه تصوير برداري شده را تعيين مي كنند. يـك سيـستم بينـايي كـامپيوتري عمومـاً شـامل پـنج جـزء اصـلي اسـت :
تجهيزات روشنايي، سنسورها (دوربـين)، گيرنـده تـصوير يـا رقمي ساز(Digitizer)، سخت افزار و نرم افـزار كـامپيوتري. اين مقاله ضمن معرفي اجزاء و ويژگيهاي سيستم هاي بينـايي كامپيوتري، كاربردهاي اين سيـستم هـا را در صـنايع غـذايي تشريح مي نمايد.
واژه های كليدي : بينـايي كـامپيوتري، پـردازش و آنـاليز تصوير، كنترل كيفيت مواد غذايي، كنترل فرآيند مواد غذايي

فایل PDF – در 8 صفحه- نویسندگان : سيد هاشم حسيني پرور،حميدرضا پوررضا، سيد علي مرتضوي، سيد محمد علي رضوي، الهام خاني پور

كنترل كيفيت فرآيند و فرآورده هاي غذايي با استفاده از سیستم های بینایی کامپیوتری

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


12. كنترل كيفيت قوطی های كنسرو در خط توليد با استفاده از روش های پردازش تصوير

چکيده: با توجه به افزايش جمعيت صنايع مختلف محصولات خود را به صورت بسته بندي در اختيار مشتريان قرار ميدهند محصولات ارائه شده ميبايست در بسته های شکيل، سالم و بدون خرابي جهت ارائه در بازار توليد شود. در اين تحقيق طرحي براي شناسايي انواع خرابي ها از جمله: سوراخ شدگي، شکاف و تورفتگي براي بسته بنديهاي از نوع قوطي بکار رفته كه ميتواند خرابيها را بررسي و در دسته هاي مختلف شناسايي و ارائه نمايد. به منظور بررسي انواع خرابيها و تعيين محل خرابي بر روي قوطي به كمك پردازش تصوير با روش مورفولوژي (Morphology) انجام داديم. كه با اين روش ميتوان شکل خرابي و ساير پارامترهاي مربوط به منطقه تخريب شده را بدست
آورد. و اين پارامترها شامل مشخصاتي از خرابي هستند كه با كمك آنها همراه با شبکه عصبي بتوان انواع خرابي را دسته بندي كرد. در مرحله آخر شبکههاي عصبي مختلفي به منظور دسته بندي انواع خرابيها بررسي شد. كه از اين جمله ميتوان به شبکه هاي عصبي: LMS,RLS وBPN اشاره كرد كه شبکههاي مربوط به BPN شامل: گراديان نزولي،training Batch و Newton Quasi ميباشد. ضمنا بهترين نتيجه حاصل شده از لحاظ دقت، سرعت و صحت اطالعات خروجي، شبکه LMS با دقت 9999 بدست آمده است.
كلید واژه: پردازش تصویر، مورفولوژی،قوطی، شبکه عصبی ، معیوب

فایل PDF – در 7 صفحه- نویسندگان : محمدرضا شیروانی زاده، محمدرضا احمدزاده

كنترل كيفيت قوطي هاي كنسرو در خط توليد با استفاده از روش هاي پردازش تصویر

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


13. مروری بر روش های اندازه گيری پارامترهای نخ با پردازش تصوير

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

كلمات كليدی: بینایی رایانه ای، پردازش تصویر، پارامترهای نخ.

فایل PDF – در 15 صفحه- نویسندگان : نعیمه باغشاهی، پدرام پیوندی، محمد علی توانایی

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

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

مقالات کنترل کیفیت با پردازش تصویر قسمت 1
مقالات کنترل کیفیت با پردازش تصویر قسمت 2

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

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

کلمات کليدي: تشخیص و شناسایی علائم راهنمایی و رانندگی، پردازش تصویر، منطق فازی، ویژگی های تغییرناپذیر

فایل PDF – در 12 صفحه- نویسندگان : احسان فنی، علیرضا خدایاری

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

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


2. استفاده از پردازش تصویر برای شناخت رفتار خرد ترافیک

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

واژه های کلیدی: پردازش تصویر،جابجایی وسایل نقلیه، رفتار حرکتی وسایل نقلیه، خصوصیات کلان ترافیک، خصوصیات خرد ترافیک، ردیابی وسایل نقلیه

فایل PDF – در 11 صفحه- نویسندگان : سید محمد سادات حسینی، رسول جوادیان و منوچهر وزیری

استفاده از پردازش تصویر برای شناخت رفتار خرد ترافیک

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


3. امکان سنجي درجه بندي کيفي سيب با استفاده از پردازش تصوير

چکيده: سيستمهای ماشين بينايي و پردازش تصوير روشهاي نويني هستند که در بخش کشاورزي کاربردهاي مختلفـي دارنـد. از سيستم ماشين بينايي براي درجه بندي محصولات مختلف استفاده ميشود. هدف اين تحقيق بررسي امکان اسـتفاده از پردازش تصوير براي درجه بندي سيب بر اساس صدمات سطحي بود. بدين منظور يک سيستم کامل ماشين بينايي شامل محفظه نوردهي، دوربين و کامپيوتر فراهم شد. نرم افزار Matlab براي پردازش تصاوير به کار گرفتـه شـد. تعـداد ۱۰۵ عددسيب گراني اسميت به طور تصادفي انتخاب شدند و از هر سيب در شرايط نوردهي، تصويرگرفته شد. سپس به کمک روش سعي و خطا مقدار آستانه به عنوان معياري براي تصميم گيري معيوب يا سالم بودن سيب به دسـت آمـد. از جملـه مشکلات در ارتباط با درجه بندي سيب وجود دمگل بود که در تصوير باينري با نـواحي معيـوب اشـتباه گرفتـه مـيشـد بنابراين نسبت طول به ضخامت براي حذف دمگل انتخاب شد. سپس سيب ها به چهار درجه عالي، درجه يک، درجـه دو و درجه سه درجه بندي شدند. به منظور ارزيابي سيستم، نتايج درجهبندي ديد انساني با نتايج درجهبندي ماشين بينـايي بـا هم مقايسه شدند. دقت حذف دمگل ۰۴/۹۹ % و دقت کلي درجه بندي ۲۳/۹۵ % به دست آمد.

واژه هاي کليدي: پردازش تصوير، درجه بندي سيب، ماشين بينايي

فایل PDF – در 11 صفحه- نویسندگان : راضيه پوردرباني، حميدرضا قاسم زاده، علي آقا گل زاده و حسين بهفر

امکان سنجي درجه بندي کيفي سيب با استفاده از پردازش تصوير

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


4. اندازه گیری وکنترل کیفی پارامترهای هندسی چرخدنده مارپیچ از طریق پردازش تصویر

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

كلید واژه ها: چرخدنده، بینایی ماشین، آستانه گیری، کالیبراسیون تصویر، لبه یابی

فایل PDF – در 9 صفحه- نویسندگان : مهران محبوبخواه و توحید کریم بابازاده ممقانی

اندازه گیری وکنترل کیفی پارامترهای هندسی چرخدنده مارپیچ از طریق پردازش تصویر

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


5. برآورد حجم سیب زمینی با استفاده از پردازش تصویر

چکیده: محاسبه حجم محصولات کشاورزی به روش ریاضی، به دلیل شکل هندسی نامنظم آنها چندان دقیق نیست. یکی از راه حل های ممکن، پردازش تصویر در ماشین های جداساز پیوسته بر اساس بینایی ماشین است. هدف از این تحقیق، یافتن روشی مناسب برای برآورد حجم سیب زمینی با استفاده از پردازش تصویر است. به کمک یک دوربین دیجیتال و یک آینه تخت، از هر نمونه تنها یک تصویر از دو نمای آن تهیه شد. با کار برد نرم افزار MATLAB، تصاویر پردازش و ابعاد سیب زمینی بر حسب موقعیت لبه در ماتریس تصویر اندازه گیری شد. در این پژوهش حجم سیب زمینی پس از تصویر برداری، با دو روش برآورد شد: روش اول، تعیین رابطه ی تجربی مبتنی بر برآورد حجم بر اساس سه قطر اصلی و روش دوم، تقسیم تصویر به قطعات کوچک تر به شکل مخروط ناقص با مقطع بیضی و برآورد حجم از مجموع حجم قطعات. اندازه سه قطر اصلی و طول قطعات مخروط ناقص، با پردازش تصویر تعیین شدند. با اندازه گیری حجم واقعی سیب زمینی از طریق جابهجایی آب میزان خطای هر دو روش محاسبه و مقایسه شد. د این تحقیق، 50 عدد سیب زمینی(رقم مارفونا) به عنوان نمونه های مورد آزمایش انتخاب شدند. نتایج نشان داد که روش تقسیم تصویر بر 64 قسمت، حجم را با دقت بالاتری (خطای حدود 8/15 درصد) نسبت به روش رابطه ی تجربی (خطای 20/5 درصد) برآورد می کند. بنابراین، برای درجه بندی سیب زمینی بر اساس حجم، روش تقسیم تصویر به عنوان روش کاربردی پیشنهاد می شود.

واژه های کلیدی: بینایی ماشین، درجه بندی، قطر اصلی، مخروط ناقص

فایل PDF – در 14 صفحه- نویسندگان : جعفر امیری پریان، محمد هادی خوش تقاضا، احسان الله کبیر و سعید مینایی

برآورد حجم سیب زمینی با استفاده از پردازش تصویر

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


6. برآورد میزان شمارش کلى میکروبى میگوى پرورشى (گونه وانامى) به کمک پردازش تصویر

چکیده: افزایش میزان تولید و مصرف میگو، اهمیت تازگى و کیفیت این محصول غذایى را براى صنعت میگو دو چندان کرده است. امروزه تعیین شاخص هاى کیفى یکى از موضوع هاى جدید و مورد علاقه بسیارى از مهندسان صنایع غذایى و بیوسیستم است، زیرا دانستن میزان کیفیت مواد غذایى مى تواند اطلاعات بیش ترى درباره شرایط نگه دارى و نظارت آن، ارائه نماید. اهمیت مقوله پردازش تصاویر در کنترل کیفیت بدین جهت است که تعیین کیفیت مواد غذایى با هزینه کم تر و راحت تر مى باشد و بدین ترتیب مواد غذایى از آسیب هاى مکانیکى ناشى از آزمون هاى شیمیایى مصون مى ماند. در این میان، یکى از شاخص هاى تازگى مواد غذایى به خصوص در آبزیان، شمارش کلى میکروبى است. لذا در این مطالعه، سعى شده تا مدلى براى پیش بینى مقادیر شمارش کلى میکروبى با کمک پردازش تصویر و شبکه هاى عصبى مصنوعى براى میگوى وانامى در طى چهار مرحله نگه دارى (روز اول، سوم، ششم و نهم) با شرایط نگه دارى مرسوم (در یخ و با صفر تا 2 درجه سانتى گراد) برآورد شود. پس از انتخاب بهترین شرایط نورپردازى، تصویر بردارى به وسیله یک دوربین دیجیتال و در دو نماى بالا و کنار انجام گرفت. این تصاویر به رایانه انتقال داده شدند. سپس براى استخراج ویژگى هاى تصویر از جعبه ابزار پردازش تصویر نرم افزار متلب استفاده شد. مقدار*a و *b به دست آمده از تصاویر بالا، در طول نگه دارى در سطح احتمال (05/0<p (به صورت خطى افزایش داشته است. با مقایسه نتایج به دست آمده از آزمون دانکن، مى توان دریافت که بین تغییر رنگ میانگین R ،واریانس *b ،میانگینV ،میانگین Y ،میانگین y ،میانگین *b و میانگین l از تصاویر گرفته شده از بالا، در مدت نگه دارى با شمارش کلى میکروبى ارتباط قوى وجود دارد. بنابراین، مى توان
از پردازش تصویر با استفاده از پارامترهاى رنگ و بافت میگو به عنوان روشى غیرمخرب، کم هزینه و آسان براى ارزیابى سریع شمارش کلى میکروبى میگو در صنایع غذایى و کنترل کیفیت مواد غذایى استفاده کرد.
واژ ه هاى کلیدي: میگو، شمارش کلى میکروبى، کیفیت، پردازش تصویر، شبکه هاى عصبى مصنوعى.

فایل PDF – در 14 صفحه- نویسندگان : رضا گلى، مهدى قاسمى ورنامخواستى، مریم میرزایى، سید سعید محتسبى

برآورد میزان شمارش کلى میکروبى میگوى پرورشى (گونه وانامى) به کمک پردازش تصویر

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

 

مقالات کنترل کیفیت با پردازش تصویر قسمت 1
مقالات کنترل کیفیت با پردازش تصویر قسمت 2

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

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

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

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

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

public static void PrintNumbers()
{
    for (int counter = 1; counter &amp;amp;lt; 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 &amp;amp;lt; 10; counter++)
    {
        Console.WriteLine(counter);
        Thread.Sleep(200);
    }
}

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

منبع


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

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

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

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

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

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

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

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

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

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

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

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

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

برای پی بردن به این موضوع که کدامیک از دو تکنولوژی CCD یا CMOS بهتر است و تصویر بهتری در اختیار کاربر قرار میدهد، لازم است تا ابتدا نحوه ی عملکرد هرکدام از این تکنولوژی ها را فرا بگیریم :

CCD چطور کار میکند ؟

نحوه کار CCD

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

CMOS چطور کار میکند ؟

نحوه کار CMOS

تفاوت اصلی تکنولوژی CMOS با CCD همانطور که در شکل کاملا مشخص می باشد در این است که فتودیودها و ترانزیستورها در تکنولوژی CMOS بر روی یک برد سوار میشوند.

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

تفاوت CCD و CMOS

مقایسه CCD با CMOS :

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

بطور کلی اگر بخواهیم این دو تکنولوژی را با هم مقایسه کنیم میتوانیم به موارد زیر توجه کنیم :
• دوربین های CMOS حساسیت به نور کمتری دارند، لذا تصاویر دریافت شده از کیفیت پایینتری نسبت به تکنولوژی همرده در دوربین های CCD برخوردار است.
• دوربین های CMOS حساسیت بیشتری به نویز دارند.
• دوربین های CMOS در هنگام استفاده از سیستم “دید در شب” چون از راهکار “مادون قرمز” استفاده میشود که نور تک رنگ با قدرت کم در محیط وجود دارد، تصاویر مناسبی ارائه نمیدهند.
• دوربین های CMOS ارزانتر از دوربین های CCD میباشند و این به دلیل عدم استفاده از یک بورد جداگانه برای عمل پردازش تصویر می باشد.
• دوربین های CMOS مصرف انرژی کمتری نسبت به دوربین های CCD دارند و درصورتی که شما از سیستم های امنیت تصویری با باتری یا سیستم پشتیبان ( یو پی اس ) استفاده میکنید بکاربردن دوربین های CMOS به صرفه تر است.
• قابلیت های دوربین های CMOS برای تشخیص چهره، حرکت و … پایین تر از دوربین های CCD می باشد.
• دوربین های CCD با سرعت بیشتری عمل Shuttering را انجام می دهند به این معنی که عمل تصویربرداری از اجسام متحرک بهتر و واضح تر انجام می شود.

منبع

در دوربین های مداربسته ی آنالوگ، رزولوشن با TV Line اندازه گیری می شود. در اکثر دوربین های آنالوگ جدید، TVL بین 420 تا 700 متغیر است. اگر چه 420TVL پایین ترین محسوب می شود، سیستم های امنیت تصویری وجود دارد که لازم است فقط یک فاصله ی کوتاه پوشش داده شود و در اینگونه موارد دوربین های 420TVL خوب کار می کنند.

دوربین های 600TVL به بالا، تصاویر دقیق تر و کنتراست بالایی دارند. اگر نیاز به تصویر صاف و دقیق دارید، دوربین های بالای 600TVL را انتخاب کنید هر چند فاکتور های مهم دیگری مانند میزان لوکس، WDR و … در تصویر دریافتی از دوربین تاثیر گذار هستند.

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

بهترین دوربین ها با بالاترین کیفیت هم در صورتی که از یک DVR با توانایی تبدیل پایین استفاده شود نمی تواند تصویر خوبی به شما بدهد. DVR ها دو مدل رزولوشن پرکاربرد دارند : D1 , Cif

Cif سایز 320*240 پیکسل به شما می دهد و D1 سایز 720*480 و همانطور که مشخص است، D1 چهار برابر بزرگتر از Cif است پس تصویر کمتر فشرده می شود و در نتیجه جزئیات بیشتری را نشان می دهد.

برای انتخاب DVR، رزولوشن ضبط تصاویر را در نظر داشته باشید. بعضی از DVR ها هر دو گزینه را دارند ولی معمولا تعداد فریم بر ثانیه را پایین می آورد تا تصاویر پر کیفیت تری ارائه دهد.

 

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

 

دوربین های آی پی (IP) قابلیت این را دارند که تصاویر با رزولوشن خیلی خیلی بالاتر داشته باشند. اولین چیزی که باید در مورد دوربین های آی پی بدانید این است که تصاویر را به صورت دیجیتال دریافت می کند به همین خاطر نیازی به تبدیل یا فشرده سازی وجود ندارد. دومین مسئله این است که دوربین های آی پی تصاویر را از طریق کابل های Ca45 یا Ca46 منتقل می کند که قابلیت گذردهی خیلی بیشتری دارند.

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

با این وجود اگر حتی تصاویر 1280*1024 پیکسل هم برایتان کوچک است می توانید از دوربین های 3 مگا پیکسل که رزولوشن 2048*1536 پیکسل دارند استفاده کنید یا حتی اگر این هم کم است می توانید دوربین های 5 مگا پیکسل (1944*2592) استفاده کنید.

خلاصه اینکه با دوربین های آنالوگ و استفاده از DVR شما نهایت تصاویر D1, 4Cif, 2Cif ,Cif خواهید داشت که ممکن است بتوانند نیازهای شما را برآورده کنند. ولی اگر نیاز به رزولوشن بالاتری دارید باید دوربین های آی پی را در نظر بگیرید و از آنها استفاده کنید.

 

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

 

موازی سازی(Parallelism) چیست؟

ﺷﺮﮐﺖ ﻫﺎی ﺗﻮﻟﯿﺪ ﮐﻨﻨﺪه ﭘﺮدازﺷﮕﺮ ﺑﺮای اﻓﺰاﯾﺶ ﺳﺮﻋﺖ ﭘﺮدازﻧﺪه ﻣﺠﺒﻮر ﺑﻪ ﺑﺎﻻﺑﺮدن ﻓﺮﮐﺎﻧﺲ ﭘﺮدازﺷﮕﺮ ﺑﻮدﻧﺪ. ﯾﮏ راه اﻓﺰاﯾﺶ وﻟﺘﺎژ ﻣﺼﺮﻓﯽ ﭘﺮدازﻧﺪه ﺑﻮد ﮐﻪ دارای ﻧﻘﺎط ﺿﻌﻔﯽ ﻣﺎﻧﻨﺪ اﻓﺰاﯾﺶ دﻣﺎ و اﻓﺰاﯾﺶ ﻣﺼﺮف ﺑﺎﻃﺮی ﻧﯿﺰ ﺑﻮد. از ﻃﺮﻓﯽ ﺗﻮﻟﯿﺪ ﮐﻨﻨﺪﮔﺎن ﭘﺮدازﻧﺪه ﺑﻪ ﮐﻤﮏ ﺑﺮﻧﺎﻣﻪ ﻧﻮﯾﺴﺎن ﭘﯽ ﺑﻪ ﺑﯿﮑﺎری زﯾﺎد ﭘﺮدازﺷﮕﺮﻫﺎ در زﻣﺎن ﺳﻮﯾﭻ ﮐﺮدن ﻓﺮاﯾﻨﺪ ﻫﺎ و ﻧﺦ ﻫﺎ ﺷﺪﻧﺪ ﮐﻪ ﺣﺪود ﻧﯿﻤﯽ از زﻣﺎن ﭘﺮدازش را ﺑﻪ ﻫﺪر ﻣﯽ داد. ﺑﺮاي ﺟﺒﺮان ﺣﺎﻓﻈﻪ ﮐﺶ را ﮔﺴﺘﺮش دادﻧﺪ اﻣﺎ ﺑﻪ دﻟﯿﻞ ﮔﺮان ﺑﻮدﻧﺶ ﺑﺎز دﭼﺎر ﻣﺤﺪودﯾﺖ ﺑﻮدﻧﺪ. ﺑﻨﺎﺑﺮاﯾﻦ ﭘﺮدازﻧﺪه ﻫﺎﯾﯽ ﺗﻮﻟﯿﺪ ﮐﺮدﻧﺪ ﮐﻪ ﺑﺘﻮاﻧﺪ ﭘﺮازش ﻣﻮازي را (در اﺑﺘﺪا) در دو ﻫﺴﺘﻪ ﺑﻪ اﺟﺮا ﺑﺮﺳﺎﻧﻨﺪ.

ﻧﺎم اﯾﻦ ﻫﺴﺘﻪ ﻫﺎ ﻫﺴﺘﻪ ﻫﺎی ﺳﺨﺖ اﻓﺰاری ﯾﺎ ﻓﯿﺰﯾﮑﯽ ﮔﺬاﺷﺘﻨﺪ. اﻧﺪك زﻣﺎﻧﯽ ﺑﻌﺪ ﻓﻨﺎوری ای ﺑﺮای رﺳﯿﺪن ﺑﻪ ﭘﺮدازش ﻣﻮازی اﻣﺎ در ﺳﻄﺢ ﻣﺤﺪود 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 تعریف شده است.

1-به صورت Function

2- به صورت Delegate

3-به صورت لامبدا

کد این قسمت به صورت زیر می باشد

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(() = &amp;amp;amp;amp;amp;amp;amp;gt; 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() می توانید چندین متد را مانند شکل زیر به صورت همزمان اجرا کنید.

Parallel.Invoke(    
() = > Method1(mycollection),    
() = > Method2(myCollection1, MyCollection2),    
() = > Method3(mycollection));

 MaxDegreeOfParallelism

ماکزیمم تعداد پردازش های موازی را مشخص می کند در کد زیر و در داخل 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();

منبع

 


فایل ضمیمه این آموزش

TPL_part_1_creating_simple_tasks

رمز فایل: behsanandish.com


دانلود کتاب آموزش برنامه نویسی موازی با #C

Parallel Programming In Csharp

رمز فایل: behsanandish.com

 

تکنولوژی EFFIO-V و EFFIO-A چیست؟

تکنولوژی EFFIO-V و EFFIO-A که توسط شرکت سونی به بازار عرضه شده اند با قابلیت و عملکردهای بسیار توسعه یافته به عنوان نسل سوم سری EFFIO در نظر گرفته می شود.

این دو محصول جدید میتوانند با سنسور تصویر 960H ترکیب شده تا بتوانند کیفیت تصویری بالاتر از 700 تی وی لاین را ایجاد نماید. همچنین این آی سی ها بهبود یافتند تا قابلیت هایی از قبیل کاهش نویز سه بعدی (3D-NR)و قرار گرفتن در معرض مادون قرمز و طیف گسترده ای پویا (WDR) و یا مه زدا DEFOG)) و نمایش تصاویر در شرایط مختلف مانند نور کم ، نور مادون قرمز و نور زیاد که کاملا توسعه یافته در عملکرد خود داشته باشند. علاوه بر این این آی سی ها برای اولین بار قابلیت تشخیص اتوماتیک به صورت صنعتی و انتخاب صحنه را دارا هستند.

EFFIO-V و EFFIO-A ، اصلاح شده تا پردازشگر سیگنال را با وضوح بالا انجام دهد و رزولوشن بالای 700تی وی لاین را ایجاد کند که این رزولوشن بالاتر از رزولوشن سری های EFFIOموجود 650 تی وی لاین است .

EFFIO-V و EFFIO-A

قابلیت کاهش نویز سه بعدی

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

این محصولات عملکرد مناسب در هر شرایطی برای تنظیم تصاویر را دارا هستند . بعضی از نصب ها زمان زیادی برای تنظیم چند دوربین با ویژگی های مختلف برای کیفیت بهتر را از ما می گیرند . اما EFFIO-V و EFFIO-A قابلیت تشخیص اتوماتیک صحنه را دارند و فقط نیاز به یک عملکرد برای 40الگو در صحنه های تصاویر را برای تصویری ایده آل مثل محدوده دینامیکی ، درجه حرارت ، رنگ را دارد. قابلیت انتخاب صحنه از پیش تنظیم شده برای صحنه های عمومی از جمله دوربین های محیط داخل و محیط خارج نصب شده یا نظارت ترافیکی و یا نور پس زمینه تنظیمات اتوماتیک آنها بر پایه تنظیمات برای کیفیت تصویر ایده آل می باشد .

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

 

EFFIO-V و EFFIO-A

 

 

 

استفاده از EFFIO-V و EFFIO-A

استفاده از EFFIO-V و EFFIO-A

 

 

تاری در سایر ccd ها

سایر ccd ها

 

 

منبع

 
Item Effio-V Effio-A
Supported CCDs 760 H, 960 H WDR/normal CCD 760 H, 960 H normal CCD
 

 

 

 

 

 

 

 

 

 

Functions

Resolution Horizontal over 700 TV lines
WDR
ATR-EX2
Noise reduction 2D-NR, 3D-NR
Day & Night
Polygon privacy mask Up to 20 masks
E-zoom
Slow shutter
Digital image stabilizer
BLC/HLC
Automatic scene detection function
Scene selection function
AF detector
Motion detection
White pixel detection compensation Static and dynamic
OSD Flexible 8 languages
Lens shading compensation
Defog
Automatic mechanical iris adjustment
External synchronization LL, VSL
RS-485
Coaxial communication ✔ (Coaxitron by Pelco)
Outputs Analog outputs Y/C separate, composite
Digital outputs ITU-R BT.656 compliant
(27 MHz / 36 MHz)
Package 97-pin LFBGA

چرا از فضای رنگی مختلف استفاده می کنیم؟

از فضای رنگی مختلف استفاده می کنیم چون این امکان در آن فضای رنگی به ما داده میشه تا بتوینم رنگ دلخواه مان را به راحتی از محدوده دیگر رنگ ها جدا کنیم .فرض کنید که شما قصد دارید رنگ سبز را در تصویر فیلتر نمایید این بازه شامل طیفی می باشد که یک سمت آن سبز تیره و در سمت دیگر آن سبز روشن می باشد برای جدا کردن آن در فضای رنگی 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 &amp;amp;lt;opencv2/core/core.hpp&amp;amp;gt;
#include &amp;amp;lt;opencv2/highgui/highgui.hpp&amp;amp;gt;
#include &amp;amp;lt;opencv2/imgproc/imgproc.hpp&amp;amp;gt;

#include &amp;amp;lt;iostream&amp;amp;gt;
#include &amp;amp;lt;vector&amp;amp;gt;

void detectHScolor(const cv::Mat&amp;amp;amp; image, // input image
double minHue, double maxHue, // Hue interval
double minSat, double maxSat, // saturation interval
cv::Mat&amp;amp;amp; 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&amp;amp;lt;cv::Mat&amp;amp;gt; 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 &amp;amp;lt; maxHue)
hueMask = mask1 &amp;amp;amp; 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 &amp;amp;amp; mask2;

// combined mask
mask = hueMask&amp;amp;amp;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&amp;amp;lt;cv::Mat&amp;amp;gt; 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,
160, 10, // hue from 320 degrees to 20 degrees
25, 166, // 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&amp;amp;lt;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();
}

منبع

 

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

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

 

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

#include "LVServerDefs.h"
#include "math.h"
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;


#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()-&gt;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-&gt;biWidth, pbi-&gt;biBitCount);
}

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

static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
    return DibRowPadding(pbi-&gt;biWidth, pbi-&gt;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 &amp; 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX &lt; 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu-&gt;AppendMenu(MF_SEPARATOR);
			pSysMenu-&gt;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 &amp; 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(&amp;rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

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

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

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

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

	// Kill program
	DestroyWindow();	

	

}

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

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

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

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

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

			}
			break;

		default:
			break;
	}	
}

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

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

	if(sz.cx | sz.cy) {
		m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader-&gt;biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapInfoHeader-&gt;biWidth = sz.cx;
		m_destinationBitmapInfoHeader-&gt;biHeight = sz.cy;
		m_destinationBitmapInfoHeader-&gt;biPlanes = 1;
		m_destinationBitmapInfoHeader-&gt;biBitCount = 24;
		m_destinationBitmapInfoHeader-&gt;biCompression = 0;
		m_destinationBitmapInfoHeader-&gt;biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapInfoHeader-&gt;biXPelsPerMeter = 0;
		m_destinationBitmapInfoHeader-&gt;biYPelsPerMeter = 0;
		m_destinationBitmapInfoHeader-&gt;biClrImportant = 0;
		m_destinationBitmapInfoHeader-&gt;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-&gt;GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC-&gt;GetSafeHdc(),
		m_rectForProcessedView.left,				// videoportal left-most coordinate
		m_rectForProcessedView.top,					// videoportal top-most coordinate
		m_rectForProcessedView.Width(),				// videoportal width
		m_rectForProcessedView.Height(),			// videoportal height
		0,											// Row position to display bitmap in videoportal
		0,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader-&gt;biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader-&gt;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-&gt;GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);

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

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

	// End of adds
}

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

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

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

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader-&gt;biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader-&gt;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 &lt; H; row++) {
		for (col = 0; col &lt; 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-&gt;biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader-&gt;biHeight; // biHeight: number of rows
	
	for (row = 0; row &lt; H; row++) {
		for (col = 0; col &lt; 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 &lt; H-2; row++) {
		for (col = 2; col &lt; W-2; col++) {
			newPixel = 0;
			for (rowOffset=-2; rowOffset&lt;=2; rowOffset++) {
				for (colOffset=-2; colOffset&lt;=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 &lt; H-1; row++) {
		for (col = 1; col &lt; 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&lt;=1; rowOffset++) {
				for (colOffset=-1; colOffset&lt;=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 &lt; 22.5) &amp;&amp; (thisAngle &gt; -22.5) ) || (thisAngle &gt; 157.5) || (thisAngle &lt; -157.5) )
				newAngle = 0;
			if ( ( (thisAngle &gt; 22.5) &amp;&amp; (thisAngle &lt; 67.5) ) || ( (thisAngle &lt; -112.5) &amp;&amp; (thisAngle &gt; -157.5) ) )
				newAngle = 45;
			if ( ( (thisAngle &gt; 67.5) &amp;&amp; (thisAngle &lt; 112.5) ) || ( (thisAngle &lt; -67.5) &amp;&amp; (thisAngle &gt; -112.5) ) )
				newAngle = 90;
			if ( ( (thisAngle &gt; 112.5) &amp;&amp; (thisAngle &lt; 157.5) ) || ( (thisAngle &lt; -22.5) &amp;&amp; (thisAngle &gt; -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 &lt; H - 1; row++) {
		for (col = 1; col &lt; W - 1; col++) {
			edgeEnd = false;
			if (gradient[row][col] &gt; 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 &lt; H; row++) {
		for (col = 0; col &lt; 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) &amp;&amp; (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) &amp;&amp; (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) &amp;&amp; (*(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 &lt; H - 1; row++) {
		for (col = 1; col &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
		
	/* Determine edge directions and gradient strengths */
	while ( (edgeDir[newRow][newCol]==dir) &amp;&amp; !edgeEnd &amp;&amp; (gradient[newRow][newCol] &gt; 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 &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; 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) &amp;&amp; !edgeEnd &amp;&amp; (*(m_destinationBmp + i) == 255)) {
		if (colShift &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;	
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	while ((edgeDir[newRow][newCol] == dir) &amp;&amp; !edgeEnd &amp;&amp; (*(m_destinationBmp + i) == 255)) {
		if (colShift &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; pixelCount; count++) {
		if (nonMax[count][2] &gt; max[2]) {
			max[0] = nonMax[count][0];
			max[1] = nonMax[count][1];
			max[2] = nonMax[count][2];
		}
	}
	for (count = 0; count &lt; pixelCount; count++) {
		i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
		*(m_destinationBmp + i) = 
		*(m_destinationBmp + i + 1) = 
		*(m_destinationBmp + i + 2) = 0;
	}
}

الگوریتم Canny در سی پلاس پلاس قسمت 1
الگوریتم Canny در سی پلاس پلاس قسمت 2
الگوریتم Canny در سی پلاس پلاس قسمت 3
الگوریتم Canny در سی پلاس پلاس قسمت 4

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

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

 

CannyEdgeWeel

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

 

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

#include "LVServerDefs.h"
#include "math.h"
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;


#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()-&gt;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-&gt;biWidth, pbi-&gt;biBitCount);
}

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

static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
    return DibRowPadding(pbi-&gt;biWidth, pbi-&gt;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 &amp; 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX &lt; 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu-&gt;AppendMenu(MF_SEPARATOR);
			pSysMenu-&gt;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 &amp; 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(&amp;rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

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

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

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

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

	// Kill program
	DestroyWindow();	

	

}

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

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

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

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

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

			}
			break;

		default:
			break;
	}	
}

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

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

	if(sz.cx | sz.cy) {
		m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader-&gt;biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapInfoHeader-&gt;biWidth = sz.cx;
		m_destinationBitmapInfoHeader-&gt;biHeight = sz.cy;
		m_destinationBitmapInfoHeader-&gt;biPlanes = 1;
		m_destinationBitmapInfoHeader-&gt;biBitCount = 24;
		m_destinationBitmapInfoHeader-&gt;biCompression = 0;
		m_destinationBitmapInfoHeader-&gt;biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapInfoHeader-&gt;biXPelsPerMeter = 0;
		m_destinationBitmapInfoHeader-&gt;biYPelsPerMeter = 0;
		m_destinationBitmapInfoHeader-&gt;biClrImportant = 0;
		m_destinationBitmapInfoHeader-&gt;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-&gt;GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC-&gt;GetSafeHdc(),
		m_rectForProcessedView.left,				// videoportal left-most coordinate
		m_rectForProcessedView.top,					// videoportal top-most coordinate
		m_rectForProcessedView.Width(),				// videoportal width
		m_rectForProcessedView.Height(),			// videoportal height
		0,											// Row position to display bitmap in videoportal
		0,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader-&gt;biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader-&gt;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-&gt;GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);

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

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

	// End of adds
}

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

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

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

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader-&gt;biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader-&gt;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 &lt; H; row++) {
		for (col = 0; col &lt; 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-&gt;biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader-&gt;biHeight; // biHeight: number of rows
	
	for (row = 0; row &lt; H; row++) {
		for (col = 0; col &lt; 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 &lt; H-2; row++) {
		for (col = 2; col &lt; W-2; col++) {
			newPixel = 0;
			for (rowOffset=-2; rowOffset&lt;=2; rowOffset++) {
				for (colOffset=-2; colOffset&lt;=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 &lt; H-1; row++) {
		for (col = 1; col &lt; 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&lt;=1; rowOffset++) {
				for (colOffset=-1; colOffset&lt;=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 &lt; 22.5) &amp;&amp; (thisAngle &gt; -22.5) ) || (thisAngle &gt; 157.5) || (thisAngle &lt; -157.5) )
				newAngle = 0;
			if ( ( (thisAngle &gt; 22.5) &amp;&amp; (thisAngle &lt; 67.5) ) || ( (thisAngle &lt; -112.5) &amp;&amp; (thisAngle &gt; -157.5) ) )
				newAngle = 45;
			if ( ( (thisAngle &gt; 67.5) &amp;&amp; (thisAngle &lt; 112.5) ) || ( (thisAngle &lt; -67.5) &amp;&amp; (thisAngle &gt; -112.5) ) )
				newAngle = 90;
			if ( ( (thisAngle &gt; 112.5) &amp;&amp; (thisAngle &lt; 157.5) ) || ( (thisAngle &lt; -22.5) &amp;&amp; (thisAngle &gt; -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 &lt; H - 1; row++) {
		for (col = 1; col &lt; W - 1; col++) {
			edgeEnd = false;
			if (gradient[row][col] &gt; 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 &lt; H; row++) {
		for (col = 0; col &lt; 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) &amp;&amp; (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) &amp;&amp; (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) &amp;&amp; (*(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 &lt; H - 1; row++) {
		for (col = 1; col &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
		
	/* Determine edge directions and gradient strengths */
	while ( (edgeDir[newRow][newCol]==dir) &amp;&amp; !edgeEnd &amp;&amp; (gradient[newRow][newCol] &gt; 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 &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; 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) &amp;&amp; !edgeEnd &amp;&amp; (*(m_destinationBmp + i) == 255)) {
		if (colShift &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; 0) {
		if (col &gt; 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col &lt; W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;	
	if (rowShift &lt; 0) {
		if (row &gt; 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row &lt; H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	while ((edgeDir[newRow][newCol] == dir) &amp;&amp; !edgeEnd &amp;&amp; (*(m_destinationBmp + i) == 255)) {
		if (colShift &lt; 0) {
			if (newCol &gt; 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol &lt; W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift &lt; 0) {
			if (newRow &gt; 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow &lt; 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 &lt; pixelCount; count++) {
		if (nonMax[count][2] &gt; max[2]) {
			max[0] = nonMax[count][0];
			max[1] = nonMax[count][1];
			max[2] = nonMax[count][2];
		}
	}
	for (count = 0; count &lt; pixelCount; count++) {
		i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
		*(m_destinationBmp + i) = 
		*(m_destinationBmp + i + 1) = 
		*(m_destinationBmp + i + 2) = 0;
	}
}

الگوریتم Canny در سی پلاس پلاس قسمت 1
الگوریتم Canny در سی پلاس پلاس قسمت 2
الگوریتم Canny در سی پلاس پلاس قسمت 3
الگوریتم Canny در سی پلاس پلاس قسمت 4