کاربرد PCA در پردازش تصویر

PCA چیست؟ | کاهش بعد در پایتون | پروژه پردازش تصویر با پایتون + کد13 دقیقه مطالعه

یکی از تکنیک‌‌‌‌‌های مرسوم برای کاهش بعد، PCA یا Principal Component Analysis است. معمولا کار با داده‌‌‌‌‌های حجیم با بعدهای زیاد امکان‌‌‌‌‌پذیر نیست و نیاز داریم قبل از انجام امور پردازشی، در صورت امکان، برخی از بعدهایی را که اهمیت زیادی ندارند، حذف کنیم. در این پروژه، به عنوان نمونه، سعی کرده‌‌‌‌‌ایم، داده‌‌‌‌‌های موجود در ماتریس یک تصویر را مورد بررسی قرار دهیم و در انتها نیز یکی از بعدهای تصویر را کاهش داده‌‌‌‌‌ایم. تصویر اولیه، عکس زیر است:

تصویر اولیه قبل از پردازش تصویر

داده‌های بسیار حجیم در دنیای امروز، نه تنها چالشی بزرگ برای سخت‌افزارهای محاسباتی به حساب می‌آیند، بلکه مانعی در پیش روی الگوریتم‌های یادگیری ماشین هستند. یکی از روش‌هایی که برای کاهش حجم و پیچیدگی داده‌ها استفاده می‌شود، روش Principal Component Analysis یا PCA است. در آنالیز PCA، هدف ما یافتن الگوهای مختلف در داده‌های مورد نظر است؛ به طور دقیق‌تر، در این آنالیز سعی بر آن است که همبستگی (correlation) میان داده‌ها به دست آید. اگر در دو یا چند بعد مشخص، بین داده‌های ما، همبستگی شدیدی وجود داشته باشد، می‌توان آن بعدها را به یک بعد تبدیل نمود؛ به این ترتیب و با کاهش بعد، پیچیدگی و حجم داده‌ها به شدت کاهش می‌یابد. اگر بخواهیم هدف غایی آنالیز PCA را بیان کنیم، باید بگوییم: هدف پیدا کردن جهت (بعد) با بیش‌ترین واریانس داده‌ها و کاهش بعد است؛ به صورتی که کم‌ترین میزان داده‌ی بااهمیت از دست رود.

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

برای انجام این پروژه از ابزارهای زیر استفاده شده است که در زمان استفاده، به توضیح هر یک می‌‌‌‌‌پردازیم:

  • زبان برنامه نویسی پایتون
  • کتابخانه pillow در پایتون
  • کتابخانه numpy در پایتون
  • کتابخانه matplotlib در پایتون

وارد کردن تصویر به برنامه با استفاده از کتابخانه pillow

برای وارد کردن تصویر به برنامه، از کتابخانه‌ی pillow استفاده می‌کنیم. این کتابخانه برای انجام امور مربوط به image manipulation مانند تغییر سایز تصویر، چرخش تصویر و … استفاده می‌شود. در این‌جا تنها استفاده‌ی ما از کتابخانه‌ی PIL یا همان pillow وارد کردن تصویر به برنامه است.

در خط اول، از کتابخانه‌ی pillow، آبجکت Image را وارد می‌کنیم. سپس با استفاده از متد Image.open و وارد کردن آدرس تصویر بر روی رایانه، تصویر را به شکل یک آبجکت با نام image ذخیره می‌کنیم. توجه داریم، به علت آن که تصویر در دایرکتوری اصلی برنامه قرار دارد، نیازی به وارد کردن آدرس دقیق آن نیست. در دو خط آخر، با استفاده از متد image.size که یک تاپل شامل طول و عرض تصویر را برمی‌‌گرداند، این دو پارامتر را ذخیره می‌‌کنیم.

ذخیره تصویر به صورت ماتریس | پیش پردازش تصویر

اطلاعات موجود در یک تصویر را به اشکال گوناگون می‌‌توان نشان داد. یکی از روش‌‌های مرسوم نشان دادن داده‌‌های موجود در تصویر، استفاده از سیستم رنگی RGB است. این سه حرف سرواژه‌‌ی رنگ‌‌های Red (قرمز)، Green (سبز) و Blue (آبی) هستند. همان طور که می‌‌دانید تصاویر از واحدهای ریزی به نام پیکسل تشکیل شده‌‌اند. هر پیکسل می‌‌تواند همه یا تعدادی از این سه رنگ را با شدت‌‌های مختلف (intensity) داشته باشد. شدت هر رنگ را معمولا با عددی در بازه‌‌ی ۰ و ۲۵۵ نشان می‌‌دهند. این اعداد بیانگر طیفی است که می‌‌توان با حافظه باینری ۸ بیتی ساخت. به عنوان مثال، یک پیکسل می‌‌تواند مقادیر زیر را اختیار کند:

$ \small [R, G, B] = [25, 150, 231] $

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

$ \small [ \begin{bmatrix} 3 & 2 & 241 \end{bmatrix}, \begin{bmatrix} 231 & 150 & 25 \end{bmatrix},…, \begin{bmatrix} 250 & 30 & 12 \end{bmatrix}] $

ماتریس دوبعدی بالا شامل تعدادی ماتریس یک بعدی است. این تعداد در واقع همان تعداد پیکسل‌‌هاست؛ به عنوان مثال در یک تصویر با اندازه‌‌ی ۱۰۲۴*۷۶۸ پیکسل، ۷۸۶۴۳۲ پیکسل داریم؛ به عبارتی دیگر، برای نشان دادن عددی این تصویر، نیازمند ماتریسی دوبعدی هستیم که درون آن ۷۸۶۴۳۲ ماتریس یک بعدی قرار دارد. هر یک از این ماتریس‌‌های یک بعدی، سه عدد اسکالر دارند که بیانگر RGB یک پیکسل است.

برای کار با ماتریس‌ها در پایتون از کتابخانه‌ی numpy که به همین منظور ساخته شده است، استفاده می‌کنیم. این کتابخانه را در خط اول کد بالا و با نام اختصاری np اضافه کرده‌‌ایم. کتابخانه نامپای حاوی یک آبجکت مهم با نام ndarray است. با استفاده از متد np.array می‌‌توان یک آبجکت ndarray ساخت. ورودی این متد یک لیست، تاپل یا هر شیء آرایه‌‌مانند دیگری در پایتون است. ما تصویری را که به عنوان ورودی دریافت کرده بودیم، به شکل یک ndarray و با نام npimage ذخیره می‌‌کنیم. ماتریس حاصل شده، مطابق فرم دلخواه ما نیست؛ این ماتریس در حقیقت یک ماتریس سه بعدی است؛ داخل این ماتریس سه بعدی، به تعداد پیکسل‌‌های عمودی تصویر، آرایه دو بعدی وجود دارد؛ در هر یک از این آرایه‌‌های دو بعدی، به تعداد پیکسل‌‌های افقی تصویر، آرایه یک بعدی وجود دارد؛ این آرایه‌‌های یک بعدی حاوی اعداد اسکالر RGB هستند. برای روشن شدن موضوع، تصور کنید یک تصویر با ۳ پیکسل عمودی و دو پیکسل افقی داریم؛ ماتریس حاصل به شکل زیر خواهد بود:

$ \small \left. [ [ [25, 120, 251], [34, 12, 78] ], [ [243, 30, 80], \right. $

$ \small \left. [25, 150, 231] ], [ [12, 30, 250], [241, 2, 3] ] ] \right. $

همان طور که در قسمت قبلی اشاره شد، فرم دلخواه ما چنین فرمی نیست؛ بنابراین نیاز داریم تا فرم ماتریس را به شکل دلخواه درآوریم. فرم مطلوب ما به شکل زیر است:

$ \small [ \begin{bmatrix} 12 & 30 & 250 \end{bmatrix}, \begin{bmatrix} 25 & 150 & 231 \end{bmatrix},…, \begin{bmatrix} 241 & 2 & 3 \end{bmatrix}] $

برای این کار یک لیست جدید به نام arr ایجاد کرده‌‌‌ایم و با استفاده از حلقه‌‌‌های for، آن را پر می‌‌‌کنیم. در نهایت لیست arr را با استفاده از متد np.array به یک آبجکت ndarray با نام nparr تبدیل می‌‌‌کنیم. علت این کار، انجام محاسبات بعدی به صورت بهینه و با سرعت مناسب با استفاده از کتابخانه numpy است.

ترسیم داده‌ها با استفاده از matplotlib

در این مرحله می‌‌‌‌خواهیم داده‌‌‌‌‌‌‌های موجود در nparr را با استفاده از نموداری مناسب به تصویر درآوریم. این ماتریس حاوی ۷۸۶۴۳۲ ماتریس یک بعدی است که مقادیر RGB را نگه می‌‌‌‌دارند. می‌‌‌‌توان به این ماتریس‌‌‌‌های یک بعدی، به مثابه‌‌‌‌ی مختصات در صفحه R3 نگاه کرد. به این ترتیب با استفاده از تابعی که در زیر به نام show_data نوشته‌‌‌‌ایم، می‌‌‌‌توان داده‌‌‌‌‌‌‌ها را در صفحه مختصات سه بعدی رسم نمود.

برای ترسیم داده‌‌‌‌‌ها از کتابخانه‌‌‌‌‌ی matplotlib پایتون استفاده می‌‌‌‌‌کنیم. این کتابخانه، غنی‌‌‌‌‌ترین کتابخانه پایتون برای رسم انواع نمودارهاست.

با استفاده از متد plt.figure، یک چارچوب و صفحه مختصات ایجاد کرده‌‌‌‌‌ایم و در ادامه با استفاده از متد fig.add_subplot، به برنامه می‌‌‌‌‌گوییم که نمودار ما را به شکل سه‌‌‌‌ بعدی و در کل صفحه رسم کند. متد ax.scatter نیز مسئول دریافت داده‌‌‌‌‌‌‌‌‌های مد نظر ماست. در این جا پارامتر c نشان‌‌‌‌‌دهنده‌‌‌‌‌ی رنگ نمودار و پارامتر marker نشان‌‌‌‌‌دهنده‌‌‌‌‌ی علامت هر نقطه در نمودار است. در نهایت نیز پس از نام‌‌‌‌‌گذاری محورها، نمودار را رسم می‌‌‌‌‌کنیم. با فراخوانی تابع برای ماتریس nparr، نمودار زیر به دست می‌‌‌‌‌آید:

نمودار سه بعدی داده‌ها با استفاده از matplotlib

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

یافتن میانگین داده‌ها | اولین گام از انجام PCA

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

  1. میانگین گرفتن از همه داده‌‌‌‌‌ها
  2. به دست آوردن mean deviation داده‌‌‌‌‌ها
  3. پیدا کردن ماتریس کوواریانس
  4. یافتن مقادیر ویژه و بردارهای ویژه ماتریس کوواریانس
  5. ساخت ماتریس w  برای انجام تبدیل خطی از فضای R3 به فضای R2
  6. اعمال ماتریس w بر روی داده‌‌‌‌‌ها و به دست آوردن داده‌‌‌‌‌های جدید در فضای R2
  7. خروجی گرفتن از تصویر جدید به دست آمده

در گام اول و با استفاده از تابع زیر، میانگین داده‌‌‌‌‌ها را به دست می‌‌‌‌‌آوریم:

عملکرد این تابع کاملا روشن است. در ابتدا تعداد داده‌ها را که همان تعداد ستون‌های ماتریس nparr هست، در متغیر columns نگه می‌داریم و پس از آن، جمع همه ستون‌های ماتریس را بر تعداد داده‌ها تقسیم می‌کنیم تا میانگین به دست آید. میانگین داده‌ها را چاپ می‌کنیم و به ماتریس زیر می‌رسیم:

$ \small \begin{bmatrix} 101.27734595 & 99.45147271 & 119.58644272 \end{bmatrix} $

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

آموزش پایتون

ساخت ماتریس کوواریانس و کد پایتون آن

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

داده های نرمال نشده

داده های نرمال شده

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

حال می‌‌‌‌توان به سراغ یافتن ماتریس کوواریانس رفت. ابتدا توضیحاتی در مورد این ماتریس ارائه می‌‌‌‌کنیم و سپس روش به دست آوردن آن را بیان می‌‌‌‌نماییم.

اگر داده‌‌‌‌های ما n بعدی باشند، ماتریس کوواریانس، یک ماتریس n*n با ویژگی‌‌‌‌های زیر است:

-عناصر موجود بر روی قطر اصلی، نشان‌‌‌‌دهنده‌‌‌‌ی واریانس داده‌‌‌‌های هر بعد هستند.

-عناصر غیر قطر اصلی، نشان‌‌‌‌دهنده‌‌‌‌ی کوواریانس دو به دوی داده‌‌‌‌های بعدها نسبت به هم هستند.

-ماتریس کوواریانس یک ماتریس مربعی متقارن است؛ بنابراین بردارهای ویژه آن، دو به دو بر هم عمودند.

اگر ماتریس mean deviation را B بنامیم، ماتریس کوواریانس را می‌توان با فرمول زیر به دست آورد:

$ S = \frac{1}{N-1}BB^{T} $

VIII. یافتن مقدار واریانس و کوواریانس داده‌ها | کاربرد PCA در پردازش تصویر

با استفاده از کد قسمت قبل، ماتریس کوواریانس به شکل زیر به دست آمد (قسمت اعشاری حذف شده است):

$ \small \begin{bmatrix} \begin{bmatrix} 3906 & 3705 &3461 \end{bmatrix}, \\ \begin{bmatrix} 3705 & 6590 &8208 \end{bmatrix},  \\ \begin{bmatrix} 3461 & 8208 & 11124 \end{bmatrix} \end{bmatrix} $

در تکه کد بالا، واریانس و کوواریانس همه داده‌‌‌ها را چاپ می‌‌‌کنیم.

کاهش بعد با روش PCA

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

تابع زیر، مقادیر ویژه و بردارهای ویژه را در اختیار ما قرار می‌‌دهد:

با اجرای کد بالا، مقادیر ویژه و بردارهای ویژه به صورت زیر به دست می‌‌‌آید (اعشار حذف شده است):

$ \small \begin{bmatrix} 19030 & 2549 & 41 \end{bmatrix} $

$ \small \begin{bmatrix} \begin{bmatrix} -0.31 & -0.89 &0.33 \end{bmatrix}, \\ \begin{bmatrix} -0.59 & -0.91 &-0.80 \end{bmatrix},  \\ \begin{bmatrix} -0.75 & 0.45 & 0.49 \end{bmatrix} \end{bmatrix} $

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

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

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

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

حال کافی است ابتدا مقادیر ویژه و بردارهای ویژه را از بزرگ به کوچک مرتب کرده و دو بردار ویژه اول را به عنوان ستون‌‌‌‌های ماتریس w بیرون بکشیم و این ماتریس را بسازیم. در کد زیر، این فرایند را انجام داده‌‌‌‌ایم:

تنها یک مرحله دیگر باقی مانده است! کافی است ماتریس w را بر روی دیتاست اعمال کنیم تا دیتای سه بعدی ما به یک دیتای دو بعدی تبدیل شود. توجه داریم که w یک ماتریس ۲*۳ است و ماتریس با سه بعدی را به ماتریس دو بعدی تبدیل می‌کند. با استفاده از قطعه کد زیر می‌توان w را از راست در دیتای قبلی ضرب کرد تا دیتاست جدید با نام new_arr به دست آید. در نهایت این دیتاست دو بعدی را در صفحه مختصات R2 رسم می‌‌کنیم:

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

نمودار دو بعدی داده‌ها با استفاده از matplotlib

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

توجه: از astype برای تبدیل datatype به unit8 استفاده می‌‌‌‌کنیم؛ زیرا کتابخانه PIL چنین داده‌‌‌‌ای را می‌‌‌‌پذیرد.

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

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

جمع بندی

در این پروژه با یکی از تکنیک‌های کاهش بعد با نام PCA آشنا شدیم و می‌توان با استفاده از آن، بعد دیتاست‌های مختلف را کاهش داد. ما در این پروژه با هدف‌های آموزشی، به طور مفصل کدنویسی کردیم؛ به جای همه‌ی این مراحل می‌توان با استفاده از کتابخانه scikit learn در پایتون، آنالیز PCA را با استفاده از کد زیر انجام داد:

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

منابع:

علیرضا کریمی
علیرضا کریمی
دانشجوی مهندسی کامپیوتر دانشگاه امیرکبیر - بنیان‌گذار فنولوژی
عضویت
اطلاع از
0 دیدگاه‌ها
بازخورد در متن
دیدن همه دیدگاه‌ها

فنولوژی را در شبکه‌های اجتماعی دنبال کنید

©۲۰۲۰ – کلیه حقوق مادی و معنوی متعلق به فنولوژی است.

عضویت در خبرنامه فنولوژی

جذاب‌ترین مطالب سایت را ماهانه دریافت کنید!

خبرنامه