اجرای نوت‌بوک‌ها تحت وب توسط سرویس Azure notebook

سرویس Microsoft Azure Notebooks یک Interactive Notebook یا یک محیط آنلاین برنامه‌نویسی پویا (interactive) قدرت گرفته از Jupyter است که زبان‌های پایتون، R و #F را پشتیبانی‌می‌کند.

مزیت این سرویس نسبت به سرویس‌های مشابه مثل mybinder و یا حتی repl.it ؛ در دسترس بودن برای همه کاربران و از پیش نصب بودن تعداد زیادی از پکیج‌های مورد نیاز برای انواع زمینه‌ها است. همچنین این سرویس یک ترمینال برای مدیریت و کار با فایل‌های نوت‌بوک در اختیار هر کاربر می‌گذارد و هر کاربر ۴ گیگابایت فضای مموری و ۱ گیگابایت فضای ذخیره‌سازی فایل برای هر نوت‌بوک به صورت رایگان در اختیار دارد.

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

 

در ادامه موارد زیر را بررسی می‌کنیم:

  • نحوه ورود و اجرای نوت‌بوک‌ها در Azure notebook و ایجاد یا کلون کردن یک نوت‌بوک
  • نصب پکیج‌ها داخل نوت‌بوک‌ها یا توسط ترمینال

 

ورود، ایجاد یا کلون و اجرای نوت‌بوک‌ها

  • پس از ورود -توسط ایمیل مایکروسافتی (outlook یا hotmail)- برای دسترسی به نوت‌بوک‌ها وارد قسمت Libraries (از گزینه‌های بالای صفحه) می‌شویم تا لیست نوت‌بوک‌ها فعلی نمایش داده شود.
  • برای ساخت و یا کلون کردن نوت‌بوک روی گزینه New Library + کلیک می‌کنیم. حال برای کلون کردن ریپازیتوری کلاس در گیت‌هاب وارد تب From GitHub شده و آدرس آن را در فیلد اول و نام دلخواه خود برای نوت‌بوکی که ساخته خواهد شد را در فیلد دوم و آدرس دلخواهمان برای دسترسی به این نوت‌بوک را در فیلد سوم وارد می‌کنیم.
  • پس از پایان عملیات کلون کردن (مدتی طول می‌کشد) وارد کتابخانه کلون شده می‌شویم و نوت‌بوک‌ها با انتخاب آنها در دسترس هستند.
  • اگر سرور اجرای نوت‌بوک‌ها روشن نباشد با انتخاب یکی از نوت‌بوک‌ها روشن شده و پس از مدتی نوت‌بوک اجرا شده و همانند یک ژوپیتر نوت‌بوک که در کامپیوتر اجرا می‌کردیم، تحت وب قابل استفاده است.
  • با باز کردن نوت‌بوک پنجره‌ای برای انتخاب کرنل مورد نیاز برای این نوت‌بوک باز خواهد شد. البته کرنل را می‌توان بعدا از طریق تب Kernel داخل خود نوت‌بوک هم تغییر داد.
  • در ویدئو برای تست نوت‌بوک و کرنل آن سه کتابخانه openCV، Tensorflow و Keras در نوت‌بوک import شده و ورژن آنها بررسی و چاپ شد.
کلون‌کردن نوت‌بوک‌ها از طریق کلیک روی دکمه launch | azurenb در ابتدای هر نوت‌بوک نیز قابل انجام است.

نصب پکیج

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

پکیج‌هایی که نصب می‌شوند تنها در طول مدت روشن بودن سرور نوت‌بوک‌ها فعال هستند و نوت‌بوک‌های غیرفعال پس از ۱ ساعت عدم فعالیت خاموش می‌شوند.

نصب پکیج از داخل نوت‌بوک

  • در داخل نوت‌بوک‌ها با استفاده از اوپراتور «!» در ابتدای خط هر سلول می‌توان دستوری را از داخل نوت‌بوک روی کرنل سیستم‌عامل سرور اجرا کرد و خروجی آن را داخل نوت‌بوک مشاهده کرد. برای امتحان این موضوع دستور cal (کوتاه کلمه calendar) استفاده شد که یک تقویم کوتاه را نمایش می‌دهد.
  • همانطور که دستور cal که یک دستور محیط ترمینال ([terminal [bash) است داخل نوت‌بوک قابل دسترسی است؛ دستورات pip یا conda که برای نصب پکیج‌ها استفاده می‌کنیم هم در دسترس هستند.
  • برای امتحان در دسترس بودن این دو دستور لیست پکیج‌های نصب شده توسط هر دو برنامه با دستورهای pip list و conda list چاپ شد.
  • همچنین برای مثال نصب پکیج، پکیج cowsay از داخل خود نوت‌بوک نصب و بلافاصله قابل import کردن و استفاده شد.

نوت‌بوک توضیحات

نکته مهم در حذف پکیج از داخل نوت‌بوک

از آنجایی که از داخل نوت‌بوک‌ها امکان دادن ورودی (input) به برنامه در حال اجرا روی ترمینال (مثلا هنگام نصب پکیج توسط pip) وجود ندارد و اگر برای حذف یک پکیج نیاز به تایید یا ورود هر نوع اطلاعاتی از سمت کاربر باشد عملیات به صورت کامل انجام نمی‌شود باید از فلگ (flag) یا تنظیم q- به همراه دستور pip استفاده کرد.

نصب پکیج توسط ترمینال

  • هر نوت‌بوک به صورت مجزا ۴ گیگ رم و ۱ گیگ فضای ذخیره‌سازی در اختیار دارد. به علاوه می‌توان به صورت مستقیم از طریق ترمینال به مدیریت و کنترل محیط اختصاصی (Environment / container) نوت‌بوک بر روی سرور پرداخت.
  • ترمینال از طریق دکمه Terminal در صفحه کتابخانه نوت‌بوک‌ها در دسترس است.
  • برای تست ترمینال مشخصات سرور چاپ شد.
  • بر اساس کرنل انتخاب شده برای نوت‌بوک، کرنل پایتون در مسیر جداگانه‌ای مطابق زیر قرار دارد:
    • کرنل python 2.7 :
      مسیر anaconda2_501/~
    • کرنل python 3.5 :
      مسیر anaconda3_420/~
    • کرنل python 3.6 :
      مسیر anaconda3_501/~
  • باید به این نکته توجه کرد که پایتون داخل نوت‌بوک با پایتونی که داخل ترمینال نصب است متفاوت است و در نتیجه برنامه‌های آنها مخصوصا pip و conda که ما نیازشان داریم هم متفاوت هستند؛ چراکه پایتون محیط ترمینال آن پایتونی است که روی سرور نصب است اما پایتونی که به عنوان کرنل نوت‌بوک استفاده می‌شود در مسیری همانند آنچه بالا گفته شد قرار دارد.
  • برای بررسی نکته بالا با استفاده از دستور which و اجرای which pip مسیر برنامه pip درون نوت‌بوک و درون ترمینال چاپ شد که مشاهده می‌شود درون نوت‌بوک pip از درون دایرکتوریanaconda3_501 و درون ترمینال از دایرکتوری دیگری اجرا می‌شوند.
  • برای دسترسی به برنامه pip یا conda ای که پکیج‌های نوت‌بوک را مدیریت می‌کند می‌توان به دو صورت عمل کرد:
    • اجرای مستقیم این برنامه‌ها از مسیر نصبشون به این صورت که بجای دستور pip یا conda می‌نویسیم : [anaconda3_501/bin/[pip or conda/~
    • همچنین می‌توان آدرس این برنامه‌ها را به متغیر (Environment Variable) ای به نام PATH اضافه کنیم تا به راحتی و تنها با نوشتن pip  یا conda در دسترس باشند. برای اینکار با وارد کردن دستور export PATH=~/anaconda3_501/bin:$PATH مسیر برنامه‌های نصب شده پایتون درون فولدر anaconda3_501 (این فولدر باید بر اساس کرنل انتخاب شده برای نوت‌بوک انتخاب شود) به متغیر PATH اضافه شده و برنامه‌های pip و conda (و دیگر برنامه‌ها را) تنها با اسمشان قابل اجرا می‌کند.
  • در ادامه برای استفاده آتی ابتدا یک جمله از شکسپیر درون فایلی نوشته شد.
  • سپس در ترمینال برای مثال نصب پکیج توسط ترمینال، پکیج lolcat نصب شد.
  • همچنین با استفاده از lolcat جمله‌ای از شکسپیر که درون فایل ذخیره شده بود چاپ شد.

نوت‌بوک توضیحات

 

آپدیت

با کلون کردن یک ریپازیتوری و اجرای نوت بوک‌های آن، تغییرات روی ریپازیتوری شامل اضافه شدن فایل‌ها، تغییر در آنها و … درون کتابخانه سرویس Azure به صورت خودکار بروز رسانی نمی‌شود. برای اینکار نیاز است تا با استفاده از git خودمان این کتابخانه (که در واقع کلونی از کتابخانه روی GitHub) است را بروز رسانی کنیم.

برای اینکار به این صورت عمل می‌کنیم که ابتدا ترمینال ریپازیتوریمون رو باز می‌کنیم (بالاتر نحوه باز کردنش توضیح داده شده) و سپس با دستور زیر به پوشه library که پوشه‌ای است که فایل‌های کلون شده از گیت‌هاب قرار داده شده اند میرویم:

→ cd library

در ادامه مطابق تصویر با دستور زیر بررسی می‌کنیم آیا تغییری در فایل‌های نوت بوک داده‌شده است یا خیر:

 git status →

همونطور که مشاهده می‌کنید تغییراتی درون فایل بعضی نوت‌بوک‌ها داده شده است که در قسمت Changes not staged for commit ذکر شده‌اند و فایل‌هایی جدید ایجاد شده‌اند که در قسمت Untracked files مشخص شده‌اند. لازم است قبل از بروزرسانی ریپازیتوری این تغییرات (فایل‌هایی که تغییر کرده‌اند) commit شده [در مورد این کار بخوانید] یا فایل‌ها به حالت اول برگردند (تغییرات آنها پاک شود) که اگر تغییرات داده شده روی نوت‌بوک‌ها مهم نیستند بهتر است همینکار را بکنیم. دلیل این‌کار این است که در طی بروز رسانی ریپازیتوری از منبع آن در گیت‌هاب تغییرات داده شده از دست نرفته و حتی بعدا قابل انتقال به دیگر نسخه‌‌های این ریپازیتوری باشند.

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

→ git checkout .

در دستور بالا به دات (. یا نقطه) در انتهای دستور دقت کنید. این نقطه به معنی اعمال checkout -در نظر نگرفتن تغییرات- به همه فایل‌های modified (فایل‌های تغییر کرده یا همان فایل‌هایی که در تصویر قبلی لیست شده بودند) است.

حال همانطور که از اجرای دوباره دستور git status مشخص است، تغییراتی که در فایل‌ نوت‌بوک‌ها داده بودیم حذف شده اند و نام هیچ فایلی در قسمت Changes not staged for commit ذکر نشده است.

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

→ git pull

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

تبدیل Affine و Perspective در OpenCV

get Perspective Transform

اگر بخواهیم یک تبدیل affine رو یک تصویر انجام بدهیم می توانیم از getPerspectiveTransform استفاده کنیم. به این صورت که مختصات چهار گوشه قسمتی از تصویر که می خواهیم تبدیل رو آن انجام شود را مشخص می کنیم (points_A) و همچنین مشخص می کنیم که می خواهیم این چهار نقطه رو چه نقاطی در تصویر جدید نگاشت شوند(points_B). تابع getPerspectiveTransform یک ماتریس 3*3 بر می گرداند که درواقع اگر هر کدام از چهار نقطه رو تصویر اصلی به فرم [xi, yi, 1] در این ماتریس ضرب شوند حاصل نقاط مورد نظر روی تصویر جدید خواهند بود که به فرم [ti*x’i, ti* y’i, ti] خواهد بود(مختصات تصویر جدید ‘x و ‘y می باشد و ti عدد ثابت است). حال برای اینکه تصویر جدید را ایجاد کنیم از getPerspectiveTransform استفاده می کنیم که به عنوان ورودی عکس اولیه و ماتریس تبدیل و اندازه عکس جدید را می گیرد و عکس جدید را برمی گرداند.

get Affine Transform

 

با استفاده از  getAffineTransform نیز می توان یک تبدیل affine انجام داد با این تفاوت که از سه نقطه استفاده می کنیم. طبق رابطه ی مشخص شده, حاصل getAffineTransform یک ماتریس 2*3 می باشد که اگر هر کدام از سه تقطه اولیه به فرم      [xi, yi, 1] در این ماتریس ضرب شوند حاصل نگاشت این نقاط در تصویر جدید به فرم [x’i, y’i] خواهد بود. با استفاده از warpAffine تصویر جدید به دست می اید که ورودی ان همان طول و عرض تصویر اصلی و تابع تبدیل(M) و تصویر اولیه می باشد.

استفاده از وبکم در OpenCV

 نحوه فعال سازی و استفاده از وبکم در  opencv

به وسیله تابع ()VideoCapture وبکم را راه اندازی می کنیم که خروجی آن یک شی (object)  می باشد که در اینجا آن را در متغیر cap قرار داده ایم.

این شئ یک متد به نام ()read دارد که با فراخوانی آن وبکم فعال می شود و شروع به دریافت فریم ها از وبکم می‌کند.

خروجی تابع ()read نوع داده ای tuple در پایتون است که در اینجا آن را در متغیرهای frame و ret ذخیره کرده ایم.

frame همان تصاویر دریافتی از وبکم است و چیزی که در ret ذخیره شده یک boolean است که نشان می دهد که آیا عملیات موفق آمیز بوده یا نه.

به وسیله تابع ()cap.release هم می توان وبکم را آزاد کرده و در نتیجه وبکم خاموش می‌شود.

به کد زیر دقت کنید:

قاعدتا میتوان روی تصاویری که از وبکم دریافت میکنیم قبل از نمایش تغییراتی ایجاد کنیم. به دو مثال زیر دقت کنید.

مثال 1) نمایش در وبکم به صورت آینه ای یا برعکس (flip)

یادآوری:

تصویر اصلی

چرخش عمودی یا Vertical Flip

چرخش افقی یا Horizontal Flip

برای flip کردن تصاویر دریافتی از وبکم از تابع ()cv2.flip استفاده می کنیم که آرگومان اول تصاویر دریافتی است و آرگومان دوم نوع flip را مشخص می کند به این صورت که:

  • اگر صفر باشد: به صورت عمودی تصاویر را flip می کند.
  • اگر مثبت باشد:به صورت افقی تصاویر را flip می کند.
  • اگر منفی باشد:به صورت هم عمودی و هم افقی تصاویر را flip می کند.

 

مثال 2) نمایش تک کاناله با وبکم

در این کد ابتدا به اندازه طول و عرض فریم یک ماتریس با مقادیر صفر می سازیم سپس هر کانال رنگی که از وبکم دریافت می شود را جدا می کنیم و در نهایت هر کانال را به صورت جداگانه نمایش می دهیم؛ به این صورت که با تابع ()cv2.merge فقط همان کانال رنگی و به جای دو کانال دیگر همان ماتریس صفری که ساخته بودیم را به عنوان ورودی می دهیم.

 

عملیات مورفولوژی

Mathematical Morphology

دو عملیات اصلی در Mathematical Morphology وجود دارد :

  • Dilation ( انشعاب )
  • Erosion ( فرسایش – تحلیل )

همچنین یک سری عملیات ترکیبی نیز وجود دارد :

  • Opening and Closing
  • Thinning and Tickening
  • و ..

Dilation 

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

Erosion

عکس عملیات Dilation می باشد .
که تصویر باینری را تحلیل میدهد ، و در واقع تصویر را کوچک میکند و قسمت های اضافی را حذف می کند . ( Shrink )

 

برای انجام این عملیات به المان هایی از جمله شکلی به عنوان فیلتر نیاز داریم که دارای ناحیه ای است که به آن Origin می گوییم  ‌.

نمونه هایی از این فیلتر ها :

 

 

انجام عملیات Dilation 

ورودی این عملیات تصویر باینری B و فیلتر S هستند.

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

 

مثال متن باینری 

 

برای مثال براثر عمل Dilation روی متن باینری مقابل :

 

 

با استفاده از فیلتر  

 

تصویر حاصل بر اثر پر کردن حفره ها و نقاط خالی نوشته ، به این شکل در خواهد آمد :

 

 

مثال : بر اثر فرایند Dilation نتیجه عملیات به چه شکل خواهد بود ؟

 

انجام عملیات Erosion :

ورودی این عملیات تصویر باینری B و فیلتر S هستند.

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

 

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

 

عملیات های Opening و Closing  

این دو عملیات از مهم ترین فرایندها روی تصاویر باینری هستند.

Opening : عملیات حاصل از یک Erosion و سپس  Dilation .

نمونه هایی از عملیات Opening 

مثال ۱ :

مثال ۲ :

اگر با استفاده از فیلترِ دیسک ۱۱ پیکسلی عملیات opening را روی تصویر اولیه داشته باشیم ، در تصویر وسط روی عکس اولیه Threshold زده شده و سپس با انجام عملیات Opening میبینیم تصویر سمت راست نتیجه می شود که بدون کوچک شدن سایز تصویر نویز ها حذف شده اند ( ابتدا Erosion برای حذف نویز ها و سپس  Dilation برای برگرداندن تصویر به سایز اولیه ) .

Closing : عملیات حاصل از یک Dilation و سپس  Closing .

نمونه هایی از عملیات Closing

مثال ۱ :

مثال ۲ :

همینطور در تصویر بالا میبینیم در تصویر وسطی روی عکس اولیه Threshold زده شده و سپس در تصویر نهایی (سمت راست) نتیجه ی عملیات Closing را میبینیم که بدون بزرگ شدن سایز تصویر حفره ها پر شدند ( ابتدا Dilation برای پر کردن حفره ها و سپس  Erosion برای برگرداندن تصویر به سایز اولیه ) .

نمونه هایی از عملیات Opening – Closing 

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

 

بررسی Opening-Closing و عملیات های Dilationو Erosion در OpenCv

برای انجام فرایند های Erosion و Dilation و Opening  و Closing در OpenCv میتوانیم به این شکل عمل کنیم :

 

با انجام این عملیات ها بر روی عکس مقابل :

 

 

نتایج بدین شکل خواهند بود  :

Erosion :

Dilation :

Opening :

Closing :

 

تصاویر دودویی و حد آستانه

به طور کلی ، عکس ها به سه دسته تقسیم می شوند :

  • باینری یا دودویی(Binary)
  • مقیاس خاکستری (Gray scale)
  • رنگی (Color)

تحلیل تصاویر باینری

زمانی که یک تصویر را آنالیز می کنیم یک سری 0 و 1 به دست می آید.
0، پس زمینه را نشان می دهد.
1، پیش زمینه را نشان می دهد.

                                    

کاربرد های تحلیل تصاویر باینری :

  • بررسی قسمت ها
  • در صنعت و تولید
  • پردازش اسناد متنی

 

(عکس سمت راست خوب باینری نشده است( مناسب نبودن مقدار حد آستانه))

 

 کارهایی که می توانیم با باینری کردن تصاویر انجام دهیم :

  • جدا کردن یک شی از پس زمینه
  • متراکم کردن پیکسل های یک شی
  • محاسبه و استخراج ویژگی های یک شی

مثال – تصویر سلول گلبول قرمز

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

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

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

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

  • آستانه گذاری یک تصویر در مقیاس خاکستری ( gray scale )
  • تعیین کردن حد آستانه مناسب
  • تحلیل اجزای متصل به هم
  • مورفولوژی دودویی ریاضی
  • انواع استخراج ویژگی، ثابت ( محیطی، مرکزی، مداری، …)

آستانه گذاری

تعریف مختصر:

برای تبدیل یک تصویر gray scale یا مقیاس خاکستری به تصویر دودویی باید یک حد آستانه (Threshold) تعیین کنیم و تمام پیکسل‌های دارای مقدار کمتر از حد آستانه را 0 و تمام پیکسل‌های دارای مقدار بیشتر از حد آستانه را 1 (یا 255) کرد.

آستانه گذاری در OpenCV:

همان طور که بیان شد برای تبدیل تصویر gray scale به تصویر دودویی از آستانه گذاری استفاده میکنیم.

در OpenCV نیز این عملیات پیاده سازی شده و اگر مقدار پیکسلی بیشتر از مقدار threshold ( حد آستانه ) باشد، یک مقدار به آن تعلق می گیرد ( ممکن است آن مقدار سفید باشد. )
درغیر آن صورت مقدار دیگری به آن تعلق می گیرد ( ممکن است آن مقدار سیاه باشد. )

تابعی که در OpenCV برای آستانه گذاری  استفاده می شود، cv2.threshold می باشد

  • پارامتر اول آن تصویر منبع (  image ) می باشد که باید یک تصویر در مقیاس خاکستری ( gray-scale ) باشد.
  • پارامتر دوم، مقدار حد آستانه ( threshold ) می باشد، که برای دسته بندی مقادیر پیکسل ها استفاده می شود.
  • پارامتر سوم، بیشترین مقدار ( Max value ) می باشد، که مقداری را نشان می دهد که اگر پیکسلی از مقدار حد آستانه ( threshold value ) بیشتر ( در مواردی ممکن است کمتر ) باشد، Max value به آن مقدار تعلق میگیرد.
  • پارامتر چهارم مربوط به نوع است. OpenCV حالت های مختلفی برای آستانه گذاری تعیین کرده است :
  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV

 

 مثال آستانه گذاری

آستانه گذاری به روش Ostu 

در آستانه گذاری سراسری ( global thresholding )، یک مقدار دلخواه برای مقدار حد آستانه ( threshold ) استفاده می شود.
سوالی که پیش می آید این است که مقدار انتخاب شده مناسب است یا خیر؟!
پاسخ : متد آزمایش و خطا
اما با فرض یک تصویر biomodel، ( به بیان ساده تر، تصویر biomodel  تصویری است که histogram دو قله ( یا حداکثر ) دارد. )
برای این تصویر ما می توانیم تقریبا حد وسط قله ها را برای مقدار حد آستانه در نظر بگیریم. یعنی کاری که در روش ostu انجام می شود. به صورت خودکار مقدار حد آستانه از تصویر histogram  برای  تصویر biomodel  محاسبه می شود.

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

نمودار هیستوگرام (Histogram) دو قسمت از گیلاس را نشان می دهد. ( پس زمینه ی مشکی حذف شده است. )

 

 

در پردازش تصویر، (balanced histogram thresholding (BHT یک متد بسیار ساده است (مثل روش ostu ) و متد lterative selection thresholding یک histogram بر مبنای متد آستانه گذاری ( thresholding ) می باشد.
این رویکرد تصویر را به دو کلاس اصلی تقسیم می کند :

  • پس زمینه ( Background )
  • پیش زمینه ( Foreground )

BHT سعی دارد سطح آستانه ( threshold ) را بهینه کند و histogram را به دو کلاس تقسیم می کند.
این متد histogram را می سنجد که بررسی می کند کدام بخش سنگین تر است و وزن را از بخش سنگین تر حذف کند تا روشن تر شود.

آستانه گذاری تطبیقی

در موارد قبلی، ما از مقدار های سراسری ( global ) به عنوان مقدار حد آستانه ( threshold value ) استفاده کردیم. اما در همه ی شرایط ممکن نیست این مقدار مناسب باشد؛ مثلا در مکانی که عکس شرایط روشنایی متفاوتی دارد، در این موارد ما به آستانه گذاری تطبیقی ( Adaptive theesholding ) رجوع می کنیم که الگوریتم، مقدار حد آستانه را برای نواحی کوچک تصویر محاسبه می کند. بنابراین ما به حد آستانه های متفاوتی برای نواحی مختلف همان تصویر می رسیم.
نتیجه: آستانه گذاری تطبیقی ( adaptive thresholding )، نتایج بهتری برای تصاویر با روشنایی متفاوت به نمایش می گذارد.

متد تطبیقی

  • cv2.ADAPTIVE_THRESH_MEAN_C  مقدار حد آستانه ( threshold )، میانگین مقادیر نواحی همسایه می باشد.
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C  مقدار حد آستانه ( threshold )، جمع وزنی مقادیر نواحی همسایه می باشد.

آستانه گذاری در پایتون و OpenCV

آستانه گذاری در پایتون و numpy

در ابتدا با numpy تصویر را باینری می‌کنیم.

در این کد با بارگذاری کردن تصویر gray scale، به رنگ های کمتر از حد آستانه ( 127 ) را صفر تعلق می گیرد ( سفید ) و به رنگ های بیشتر از حد آستانه ( 127 )  255 تعلق می گیرد.( سیاه )

نکته :
دلیل اینکه از np.copy استفاده می کنیم این است که اگر به این صورت باشد : ( Newimage = image )
در واقع به یک مکان از حافظه دو اشاره گر اشاره می کند که در این صورت عکس اصلی ( اولیه) خراب می شود.

آستانه گذاری در OpenCV

برای اینکار می‌توانیم از تابع threshold در opencv نیز استفاده کنیم

پس از بارگذاری عکس، این بار به کمک تابع threshold که در آن حد آستانه 127 تعیین شده است، به مقادیر کمتر از این مقدار 0 تعلق می گیرد و به مقادیر بیشتر از این مقدار 1 تعلق می گیرد و نوع آن به صورت باینری (سیاه و سفید) تعیین شده است. ( در اینجا ret نشان دهنده ی حد آستانه است. )

پارامترهای مختلف تابع threshold

در این کد پارامتر چهارم (threshold types) بررسی شده است.

  : cv2.THRESH_BINARY

به مقادیر کمتر از حد آستانه (  127 )،  0 تعلق می گیرد ( سفید ) و به مقادیر بیشتر از حد آستانه،  255 تعلق می گیرد ( سیاه ).

 : cv2.THRESH_BINARY_INV

به رنگ های کمتر از حد آستانه (  127 )، 255 تعلق می گیرد ( سیاه ) و به رنگ های  بیشتر از حد آستانه،  0 تعلق می گیرد ( سفید ).(برعکس مورد قبل)

 : cv2.THRESH_TRUNC

مقادیر بیشتر از حد آستانه بریده می شوند. ( 255 بدون استفاده می ماند. )

 : cv2.THRESH_TOZERO

مقادیر بیشتر از حد آستانه تغییر نمی کنند. و به مقادیر کمتر از حد آستانه، 0 تعلق می گیرد.

 : cv2.THRESH_TOZERO_INV

مقادیر کمتر از حد آستانه تغییر نمی کنند.و به مقادیر بیشتر از حد آستانه، 0 تعلق می گیرد.

یک مثال عملی از دودویی کردن تصویر در OpenCV

در ابتدا به کمک فیلتر ها نویزها را برطرف می کنیم زیرا thresholding ها local هستند باعث ایجاد نویز در تصویر می شوند.در آخر از ostu استفاده می کنیم چون ostu حد آستانه را مشخص می کند تصویر بهتری در خروجی نشان می دهد. ( ممکن است در روش های قبل threshold حد آستانه را درست تعیین نکرده باشیم. )

خروجی ها به صورت زیر است :

نتیجه گیری : ostu تصویر بهتری در خروجی نشان می دهد.

 

threshold Binary

: Adaptive thresholding

: Gaussian ostu thresholding 

 : ostu thresholding

 : Adaptive thresholding with Gaussian

دستکاری تصاویر (۵) – فیلترها و کرنل‌ها

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

برای شروع به چند تعریف نیاز داریم:

تصویر

همانطور که می‌دانید تصویر یک ماتریس است که در هر خانه مقادیر رنگ آن خانه (با توجه به سیستم رنگی مورد استفاده) قرارگرفته اند.

کانولوشن (Convolution)

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

کراس‌کرولیشن (Cross-correlation)

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

 

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

کرنل

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

 

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

اعمال کرنل روی تصویر

 

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

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

  • کانولوشن بدون حاشیه‌گذاری (Valid Padding Convolution)
    عبور کرنل به صورتی که خانه اول ماتریس کرنل (خانه [۰.۰]) بر روی خانه اول ماتریس تصویر بیافتد و کرنل از تصویر خارج نشود.
    این روش باعث از دست رفتن قسمت‌هایی از حاشیه تصویر می‌شود.
نحوه عبور یک ماتریس کرنل بر روی یک ماتریس تصویر و انجام عملیات کراس‌کرولیشن
نحوه عبور کرنل روی ماتریس تصویر و انجام عملیات کراس‌کرولیشن در حالت Valid Padding

 

  • کانولوشن با حاشیه‌گذاری (Same Padding Convolution)
    عبور کرنل به صورتی که خانه مرکز کرنل (خانه‌ای که برای نوشتن مقادیر عملیات در هر مرحله انتخاب شده) بر روی خانه اول ماتریس تصویر بیافتد.
    در این حالت کرنل می‌تواند از تصویر خارج شود که مقادیر خارج از تصویر عموما صفر در نظر گرفته می‌شوند.
نحوه عبور کرنل روی ماتریس تصویر و انجام عملیات کراس‌کرولیشن در حالت Same Padding

توجه کنید که در تصویر بالا حرکت کرنل بیشتر از یک پیکسل است؛ برای همین تصویر خروجی از ورودی کوچکتر شده است.

 

به جز دو روش فوق برای جلوگیری از کوچک شدن تصویر می‌توان از روش‌های تکرار حاشیه تصویر (Edge Value Replication) و یا گسترش آینه‌وار (Mirror Extension) نیز استفاده کرد:

از راست به چپ: گسترش آینه‌وار، تکرار حاشیه تصویر و حاشیه‌گذاری

استفاده از کرنل‌ها

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

کرنل تطابق

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

کرنل انتقال به چپ

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

 

 

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

استفاده از کرنل‌ها برای ایجاد فیلتر روی تصاویر

فیلتر محوی (Blur Filter)

با عبور یک کرنل Moving Average (کرنل تصویر پایین) که کرنلی با مجموع مقادیر ۱ است و مقدار هر خانه را با میانگین مقدار خانه‌های همسایه‌اش جایگذین می‌کند، می‌توان تصویر را محو کرد.

 

این کرنل تغییرات ناگهانی در رنگ تصویر را ملایم‌تر می‌کند. به تصویر زیر که عبور کرنل بالا بر روی یک ماتریس است دقت کنید:

 

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

 

فیلتر تیزی (Sharpen Filter)

کرنل زیر را در نظر بگیرید:

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

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

نتیجه حاصل از جدا کردن جزئیات و اضافه کردن آن به عکس را می‌توان به ترتیب در سطر‌های تصویر زیر مشاهده کرد:

درنتیجه فیلتر تیزی (Sharpen Filter) به صورت زیر است:

 

دستکاری تصاویر (۴) – عملیات ریاضی و ترکیب تصاویر رنگی در OpenCv

یادآوری چگونگی سر ریز شدن متغیر ها 

با توجه به اینکه برای تصاویر در OpenCV از نوع داده‌ای unit8 یعنی عدد صحیح بدون علامت ۸ بیتی استفاده میکنیم . پس این نوع ۳ به توان ۸ یعنی ۲۵۶ مقدار مختلف میتواند داشته باشد و مقادیر خارج از ۰ تا ۲۵۵ امکان پذیر نبود و در صورتی که عددی بزرگتر قرار نسبت داده شود متغیر سر ریز می‌شد.

به قطعه کد زیر توجه کنید.

در کد بالا ماتریس دو عنصری a که عناصر آن همه ۱ هستند ، بعد از اینکه عناصرش ۱۰۰ برابر میشوند و به علاوه ۲۵۵می شنود ماتریس [۲۵۵ ۲۵۵] ساخته میشود ولی بعد از افزایش به اندازه ی ۱۶۰ واحد در هر عنصر از ماتریس b ماتریس [۴ ۴] ساخته میشود چون سرریز صورت گرفته ( مقدار بیش از ۲۵۵) و دوباره مقدار مربوطه از ۰ شروع شده است .

خروجی کد فوق :

طبیعتا این موضوع برای جمع دو ماتریس هم اندازه نیز صادق است:

در کد بالا m1 و m2 دو ماتریس به ترتیب به مقادیر :

هستند.که بعد از عملیات جمع این دو ماتریس دو درایه ی سطر دوم به دلیل سرریز مقادیر ۰ و ۱ را میگیرند.

خروجی کد:

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

در کد مشاهده شده ماتریس m1 را داریم که می خواهیم مقدار را به تمام درایه های آن بیفزاییم . براثر این عملیات مقادیری از ماتریس اصلی که بیشتر از ۱۵۰ یا مساوی ۱۵۰ هستند وقتی با ۱۰۰ جمع شوند سر ریز میشوند . برای جلوگیری از این موضوع میتوانیم درایه هایی که مقدارشان بیشتر یا مساوی ۱۵۰ هستند برابر با بیشترین مقدار ممکن یعنی ۲۵۵ قرار داده و عملیات جمع را برای بقیه ی درایه ها انجام دهیم .

خروجی کد:

 

حل مشکل با  OpenCV :

اگر از توابع cv2.add و cv2.subtract استفاده کنیم این مشکل را مشاهده نخواهیم کرد و مقادیر کمتر از ۰ همان ۰ و مقادیر بیش از ۲۵۵ همان ۲۵۵ باقی خواهد ماند.
به کد مقابل توجه کنید :

در این قطعه کد تصویری را خواندیم و سپس ماتریسی با اندازه ی تصویر خوانده شده ساختیم که تمام عناصرش ۱۰۰ هستند ، سپس این ماتریس را با تصویرمان جمع کردیم و چون ازتابع add در opencv استفاده کردیم خرابی در تصویر نهایی مشاهده نشد ، و به طور مشابه عمل subtract را انجام دادیم .

تصویر Orginal  :

orginal

تصویر Add شده :

تصویر Subtract شده :

 

مثال عملی ترکیب تصاویر رنگی :

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

 img1 :                                                        img1  img2 :
img2

result :

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

برای حل این مشکل میتوانیم با نسبت های مختلف ، مثلا  ۰.۵ و ۰.۵ یا ۰.۳ و ۰.۷  و یا…. نسبت هایی که مشابها جمعشان  ۱ گردد تصاویر را جمع کنیم .

تابع مورد نظر به شکل زیر می باشد :

addWeighted(src1, alpha, src2, beta, gamma)

که آلفا و بتا در فرمول دوم مشاهده میشود.

g(x)=(1−α)f0(x)+αf1(x)
dst=α⋅img1+β⋅img2+γ

 با این عمل میتوانیم تاثیر هر تصویر را بعد از عملیات جمع در تصویر نهایی دستکاری کنیم :

 

 img1 :                                                        img1  img2 :
img2

result :

 

عملیات بیتی (Bitwise Operations) و نقاب‌گذاری تصویر (Masking) :

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

همانطور که قابل مشاهده است ، در صفحه ای به اندازه ی ۳۰۰ در ۳۰۰ که تمام عناصر ماتریس آن صفر هستند ( صفحه ی تمام سیاه ) یک مربع سفید در وسط صفحه از مختصات (۵۰ ، ۵۰) تا (۲۵۰ ، ۲۵۰) رسم میکنیم ، سپس در صفحه ای مشابه به رسم نیم دایره میپردازیم.

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

عملیات بیتی تصویر در OpenCV :

حال به بررسی ۴ عملیات

  • bitwise_and
  • bitwise_or
  • bitwise_xor
  • bitwise_not

روی تصاویر خواهیم پرداخت :

عملیات bitwise_and :

در نتیجه ی عملیات and بین دو تصویر قسمت هایی که پیکسل در هر دوی آن تصاویر ۱  ( سفید ) است ، سفید ( مقدار 1 ) باقی خواهد ماند ، و بقیه ی پیکسل ها صفر ( سیاه ) خواهند بود و نتیجه به این شکل خواهد بود :

عملیات bitwise_or :

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

عملیات bitwise_xor :

در نتیجه ی عملیات xor ، قسمت هایی از تصویر که مربع و نیم دایره تفاوت بیتی ( یکی صفر و دیگری یک ؛ یکی سیاه و دیگری سفید ) دارند ، سفید می شود.
نتیجه شکل زیر خواهد بود :

عملیات bitwise_not :

این تابع نیز قسمت هایی از تصویر که بیت ۰ دارند ۱ میکند و بلعکس ( پیکسل های سیاه را سفید میکند و پیکسل های سفید را سیاه )

مثال عملی با تصویر رنگی :

اگر تصویر گاو اصلی را crop کنیم و سپس با این تصویر  and کنیم :

با توجه به اینکه میدانیم هر چیزی که با ۱ ، and شود ، حاصل خود آن میشود ، و هرچیزی که با ۰ ، and شود حاصل ۰ خواهد بود پس در نتیجه تصویر به این شکل در خواهد آمد :

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

برای درک عملگر بیتی دو تصویر به مثال زیر توجه کنید :

خروجی به شکل زیر خواهد بود :

دستکاری تصویر (3) – مثال‌های عملی در OpenCV

تبدیلات همگن (Affine Transform) 

انتقال (Translation) :

برای پیاده سازی این تبدیلات  از cv2.warpAffine استفاده میکینم.

به طور مثال در کد زیر :

پس از خواندن عکس  و دریافت عرض و طول تصویر:

یک نسبت دلخواه برای انتقال طول و عرض تصویر در نظر گرفتیم :

با استفاده از ضرب ماتریس انتقال در مختصات تصویر ،تصویر انتقال پیدا میکند :

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

چرخاندن  (Rotations) :

*یادآوری

ماتریس دوران:

 

 

 

دوران در opencv

  • از نقص های این روش میتوان به ایجاد فضاهای سیاه اطراف تصویر اشاره کرد که در ادامه فضاهای سیاه ایجاد شده اطراف تصویر را با کراپ از بین میبریم.

روشی دیگر برای دوران :

ترانهاده ماتریس

ماتریس ترانهاده  با تغییر جای سطر ها با ستون ها باعث چرخش تصویر میشود و فضای سیاه اطراف تصویر ایجاد نمی کند .

flip  تصویر

تصویر را در جهت افق و خط عمود آینه می کند.

پارامتر دوم :

صفر یا false عکس را حول محور x  ها قرینه میکند.

یک یا True عکس را حول محور y ها قرینه میکند.

تغییر اندازه (scaling, resizing) و درون‌یابی (interpolations)

  • fx  و fy  پارامتر های  نام دار هستند میتوان آنها را به تابع پاس نداد.

روش های درون یابی :

  1.  Bilinear Interpolation (درج وابسته به ۴ پیکسل ۴ طرف)
  2.  Bicubic interpolation (درج وابسته به ۴ پیکسل ۴ طرف به علاوه ۴ پیکسل اریب)
  3.  Nearest Neighbor (نزدیکترین همسایه)

استفاده از ماتریس تبدیل

جهت هایی را که میخواهیم  scale شوند را در قطر ماتریس قرار میدهیم.

معایب :

  1. مشکل سیاهی تصویر را دارد.
  2. عکس بعد از اعمال interpolations باید crop شود .

Image Pyramids

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

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

کاربرد: آموزش به هوش مصنوعی برای تشخیص اشیاء

Cropping

Opencv تابع مشخص برای کراپ کردن ندارد.

روش های جایگزین :

یکی از روش های کراپ کردن slicing است که با دادن بازه ی دقیقی از  x ,y (برای ساده تر شدن عکس را بصورت دو بعدی و سیاه سفید تصور میکنیم)همان بازه ی مشخص شده را کراپ می کنیم.

نکات :

مختصات شروع و پایان حتما  باید اعداد صحیح باشند.

در بازه نویسی به صورت   [ x1 : x2 , y1:y2 ] :

x1 شامل بازه میشود اما x2 نوشته نمیشود .

روش دیگر اسلایس کردن ماتریس  ها به کمک  numpy است.

مثال:

a=np.array([[100,200,400],[1,2,3]])

میخواهیم  عناصر سطر اول [0] و ستون دوم [1]  را انتخاب کنیم.

a [0: , 1:] => array([[200, 400],[ 2, 3]])

 

دستکاری تصویر (2) – دوران، انتقال، مقیاس و درون‌یابی

Rotation (دوران)

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

تابعی که در opencv برای این کار گمارده شده است به صورت زیر است:

 

که آرگومان اول و دوم به ترتیب x و y مرکز دوران را میگیرند و آرگومان بعدی زاویه چرخش و آرگومان اخر آپشن اضافه ای در اختیار ما قرار داده است و عددی برای بزرگنمایی یا کوچک نمایی تصویر میگیرد و بدون درگیر کردن ما با ماتریس چرخش و سینوس و کسینوس این تبدیل را به خوبی انجام میدهد.=)

خروجی این تابع؛ ماتریس چرخش یا دوران می باشد.که میتوان با تابع پرینت آن را نمایش داد.

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

 

2D Translation (انتقال دوبعدی)

برای انتقال یک نقطه از مکانی به مکان دیگر از ماتریس زیر استفاده میکنیم:

که در اینجا:

x´ = x + Tx = 1 * x + 0 * y + Tx

y´ = y + Ty =  0 * x + 1 * y + Ty

و به این صورت میتوانیم تصویر را جابجا کنیم.

به صورت خلاصه تر این ماتریس به این صورت بیان میشود:

1 ها مقدار مقیاس گذاری را مشخص میکنند یعنی 1 خانه ی (0 و 0) نشان دهنده scale در راستای ایکس و 1 خانه ی (1 و 1) نشان دهنده scale  در راستای ایگرگ میباشد.که چون در اینجا مقادیر 1 هستند یعنی اندازه ی تصویر بدون تغییر میماند  و بزرگ نمایی یا کوچک نمایی انجام نمیشود.

تابعی که برای این کار در opencv در نظر گرفته اند بدین صورت است:

 

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

و خود ماتریس انتقال را میتوانیم بدین صورت بسازیم:

Np.float32([[1 , 0 , tx] , [0 , 1 , ty]])

Scalling (مقیاس گذاری)

ما اگر بخواهیم مثلا یک ماتریس 2 در 2 را به ماتریس 6 در 6 تبدیل کنیم یک سری نقاط(پیکسل) را نداریم.

با این 4 روش میخواهیم این نقاط را بسازیم.

  1.  Nearest Neighbor (نزدیکترین همسایه)
  2.  Bilinear Interpolation (درج وابسته به ۴ پیکسل ۴ طرف)
  3. Bicubic interpolation  (درج وابسته به ۴ پیکسل ۴ طرف به علاوه ۴ پیکسل اریب)
  4. استفاده از ماتریس تغییر شکل (با استفاده از تابع warpaffine با ارقام دلخواه و ترتیب گفته شده در ماتریس scalling، که در ابتدا آمده است)

این مقیاس گذاری دو حالت دارد:

  • بزرگ نمایی
  • کوچک نمایی

روش نزدیکترین همسایه(ساده ترین روش):

در این روش مثلا فلان پیکسل را نسبت می بندیم که آن روی یک پیکسل متناظرش در ماتریس جدید می افتد پس همان مقدار پیکسل را به آن میدهیم.سپس پیکسل دیگری را بررسی میکنیم که مثلا هیچ پیکسلی را متناظرش در ماتریس جدید نمی یابیم و در آن پیکسل مقدار نزدیکترین پیکسل را قرار می دهیم.

روش درج وابسته به۴ پیکسل ۴ طرف:نسبت به روش قبلی دقیقتر است.همانطور که مشخص است مقادیر 4 پیکسل اطراف را میانگین گرفته و پیکسل های خالی را پر میکنیم.

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

حال میخواهیم تابعی در opencvکه این کار را انجام میدهد بیان کنیم:

 

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

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

در آخر مقایسه انواع  روش های scaleرا باهم میبینیم:

دستکاری تصویر (1) – ماتریس‌های تبدیل

در جلسات قبل با مفهوم فضاهای رنگی آشنا شدیم و خواندیم که هر عکس رنگی از سه کانال رنگی (R,G,B) تشکیل شده است که هر کدام می توانند از صفر تا 255 مقدار بگیرند .

و اگر ما دستور image.shape  را برای یک عکس رنگی چاپ کنیم خروجی اینچنین خواهد بود

(height,width,3)

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

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

 

که خروجی آن برای هر کانال به صورت زیر خواهد بود:

 

 

تا اینجای کار با مفاهیم اولیه ی open cv  آشنا شدیم اکنون میخواهیم وارد مبحت image manipulations بشیم که لازمه ای این مبحث این هستش که یه دوره ی سریعی روی مباحث جبر خطی که عمدتا در هندسه تحلیلی عنوان شده داشته باشیم .

انواع روش های image manipulations  یا دستکاری تصویر:

  • انتقال های همگن یا غیر همگن ( (Transformations, affine and non affine
  • تبدیلات (Translations)
  • Rotations (دوران)
  • Scaling, re-sizing and interpolations  ( درون یابی یا interpolations به معنای این است که در هنگام تغییر سایز عکس پیکسل هایی که خالی میماند را با چه الگوریتمی میخواهیم پر کنیم.)
  • Image Pyramids

انتقال

Transformations ها کلا تبدیلات ماتریسی هستند که عموما به صورت ضرب دو ماتریس هستند که ماتریس سومی را به عنوان خروجی میدهند و به دو نوع کلی همگن یا غیر همگن تقسیم میشوند.

تبدیلات همگن

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

بدین ترتیب در نتیجه یک تبدیل همگن، تمامی نقاط روی یک خط در ورودی، در خروجی نیز روی یک خط خواهند ماند.

  • انتقال، تجانس، تشابه و چرخش نمونه هایی از تبدیل های همگن هستند.

نکته هر تبدیل خطی همگن هست اما هر تبدیل همگنی خطی نیست.

 

معرفی وکتور

یک بردار یک بعدی است. که دو نوع دارد وکتور ستونی و سطری:

1)وکتور ستونی    2) وکتور سطری

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

نحوه محاسبه نرم اقلیدسی:

اگر دو وکتور به نام های A,B  داشته باشیم نرم اقلیدسی آن برابر است با

ماتریس

ماتریس همان بردار mxn است که در برنامه نویسی نیز همین معنا را دارد و یک عکس سیاه و سفید درواقع یک ماتریس  است که به میزان عرض عکس سطر و ارتفاع آن ستون دارد و هر درایه نمایانگر یک پیکسل است که میتواند ازصفر تا 255 مقدار بگیرد.

عکس رنگی نیز در واقع شامل سه ماتریس است که هر کدام از آنها مقدار یکی از کانالهای رنگی را نگه میدارد.

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

جمع ماتریس ها با یکدیگر

جمع یک ماتریس با عدد ثابت

که در آن تک تک خانه های ماتریس با عدد جمع خواهند شد.

ضرب ماتریس در یک عدد ثابت یا scale

که در آن تک تک خانه های ماتریس در عدد ضرب خواهند شد.

 

نرم اقلیدسی در ماتریس ها

که دارای ویژگی های زیر است

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

 

برای یاد آوری ضرب ماتریس ها میتوانید اینجا را کلیک کنید.

معرفی چند ماتریس خاص

ماتریس همانی:  یک ماتریس مربعی است کهتمامی درایه های روی قطر اصلی یک و سایر آنها صفر هستند.

ماتریس قطری: یک ماتریس مربعی است که درایه های روی قطر اصلی عدد دارند و سایر آنها صفر هستند.

ماتریس ترا نهاده: ماتریسی است که جای سطر و ستون هایش باهم عوض شده باشند.

ماتریس انتقال: ماتریسی است که برای انتقال بردار ها از طریق ضرب استفاده میشود.

نکاتی درباره ی ضرب ماتریس ها:

  • ضرب ماتریس ها خاصیت جابه جایی ندارد AB!=BA.
  • تنها میتوان ماتریس های مربعی را به توان رساند.