نمایش/عدم نمایش سایدبار
رفتن به بالای صفحه
أَللّهُمَّ ارْزُقْنی شَفاعَةَ الْحُسَیْنِ یَومَ الْوُرُودِ
مهدی دمیرچیلو

آموزش تبدیل فوریه در OpenCV

499

به نام خدا : تو این مطلب میخوام درباره چند تابع توضیح بدم، که مربوط هستند به این مطلب ( آموزش ماژول Operations on arrays در OpenCV )؛ که خب چون توضیحات این چندتا تابع طولانی شد، گفتم که تو یه مطلب جداگونه منتشرشون کنم؛ این مطلب مربوطه به تبدیل فوریه و تابع اصلی این مطلب هم cv::dft هستش؛ توجه داشته باشید که در این مطلب، کلمه complex به معنی “مختلط” بکار رفته و نه “پیچیده”، مختلط کلمه صحیح تری هستش در این مطلب تا پیچیده.

آموزش تبدیل فوریه در OpenCV

 

تبدیل فوریه

تبدیل فوریه

تبدیل فوریه ( Fourier Transform ) یک تصویر را به اجزای سینوسی و کسینوسی آن تجزیه میکند؛ به عبارت دیگر، یک تصویر را از حوزه فضایی ( spatial domain ) خود به حوزه فرکانس ( frequency domain ) خود تبدیل میکند؛ ایده این است که هر تابعی را می توان دقیقاً با مجموع توابع بی نهایت سینوس و کسینوس تقریب زد؛ تبدیل فوریه راهی برای انجام این کار است.

 

سری فوریه

از نظر ریاضی تبدیل فوریه تصاویر دو بعدی عبارت است از :

$$ \Large{ F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})} } $$

$$ \Large{ e^{ix} = \cos{x} + i\sin {x} } $$

در اینجا f مقدار تصویر در حوزه فضایی و F در حوزه فرکانس آن است؛ نتیجه تبدیل، اعدادی مختلط ( complex numbers ) است؛ نمایش این امر یا از طریق یک تصویر واقعی ( real image ) و یک تصویر مختلط ( complex image ) یا از طریق یک تصویر بزرگی و فاز ( magnitude and a phase image ) امکان پذیر است؛ با این حال، در سراسر الگوریتم های پردازش تصویر، تنها تصویر بزرگی ( magnitude image ) جالب است، زیرا این شامل تمام اطلاعات مورد نیاز ما در مورد ساختار هندسی تصاویر است؛ با این اوصاف، اگر قصد دارید برخی اصلاحات را در این فرم ها در تصویر ایجاد کنید و سپس باید آن را تبدیل مجدد ( retransform ) کنید، باید هر دوی این موارد را حفظ کنید.

 

تبدیل فوریه ( Fourier Transform ) برای تجزیه و تحلیل ویژگی های فرکانسی فیلترهای مختلف استفاده میشود؛ برای تصاویر، تبدیل فوریه گسسته ( DFT ) 2-بعدی برای یافتن دامنه ( domain ) فرکانسی استفاده میشود؛ یک الگوریتم سریع به نام تبدیل فوریه سریع ( FFT ) برای محاسبه DFT استفاده میشود.

برای یک سیگنال سینوسی، \( x(t) = A \sin(2 \pi ft) \)، می‌توان گفت \( f \) فرکانس سیگنال است، و اگر دامنه ( domain ) فرکانسی آن گرفته شود، می‌توانیم یک spike در \( f \) ببینیم؛ اگر سیگنال برای تشکیل یک سیگنال گسسته نمونه برداری شود، دامنه ( domain ) فرکانسی یکسانی را دریافت میکنیم، اما در محدوده \( [- \pi, \pi] \) یا \( [0,2\pi] \) ( یا \( [0,N] \) برای DFT نقطه N ) تناوبی است؛ شما می توانید یک تصویر را به عنوان یک سیگنال در نظر بگیرید که در دو جهت نمونه برداری میشود؛ بنابراین تبدیل فوریه در هر دو جهت X و Y به شما نمایش فرکانسی تصویر را میدهد.

به طور واضح تر، برای سیگنال سینوسی، اگر دامنه ( amplitude ) آنقدر سریع در مدت زمان کوتاه تغییر کند، میتوان گفت که یک سیگنال با فرکانس بالا است؛ اگر به آرامی تغییر کند، سیگنال فرکانس پایین است؛ می توانید همین ایده را به تصاویر نیز تعمیم دهید؛ در کجا، دامنه ( amplitude ) در تصاویر به شدت متفاوت است؟ در نقاط لبه ( edge ) و یا noise ها؛ بنابراین میتوان گفت لبه ها و نویزها محتوای فرکانس بالا در یک تصویر هستند؛ اگر تغییرات زیادی در دامنه ( amplitude ) وجود نداشته باشد، جزء فرکانس پایین است.

 

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

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

تجزیه و تحلیل تصویر، فیلتر کردن تصویر، بازسازی تصویر و فشرده سازی تصویر از جمله برنامه هایی هستند که از تبدیل فوریه استفاده میکنند.

تکمیل کردن این قسمت...

 

نحوه پیدا کردن تبدیل فوریه در OpenCV : برای این کار، OpenCV تابع cv::dft ( و cv::idft، که باهاش کاری نداریم! ) رو ارائه داده است، که نتیجه ای دو کاناله را بر میگرداند، کانال اول قسمت واقعی نتیجه و کانال دوم قسمت مختلط نتیجه را خواهد داشت.

1) تابع dft

1) تابع dft : این تابع، تبدیل فوریه گسسته مستقیم و معکوس را انجام میدهد ( که مستقیم یا معکوس بودنش، به کمک پارامتر flags تعیین میشه )؛ البته یه تابع دیگه با نام idft داریم که مختص تبدیل فوریه گسسته معکوس هستش که خب ازش استفاده نمیکنیم و برا تبدیل فوریه گسسته مستقیم و معکوس، از همین تابع dft استفاده میکنیم؛ ورودی این تابع میتونه 1-کاناله ( کانال واقعی فقط ) و یا 2-کاناله ( کانال واقعی + کانال مختلط ) باشه؛ خروجیش هم همینطور، میشه تعیین کرد ( به کمک پارامتر flags ) که 1-کاناله باشه یا 2-کاناله.

 

تعریف تابع :

توضیح پارامترها :

  • src : آرایه ورودی که میتواند واقعی ( real ) یا مختلط ( complex ) باشد؛ نوع آرایه ورودی باید CV_32FC1 یا CV_32FC2 یا CV_64FC1 یا CV_64FC2 باشد.
  • dst : آرایه خروجی که اندازه و نوع آن به پارامتر flags بستگی دارد.
  • flags : پرچم های تبدیل؛ که به کمک نوع شمارشی DftFlags، مقدار دهی میشه و میتونه ترکیبی از مقادیر DftFlags باشه.
  • nonzeroRows : ؟؟؟؟؟ ( نیاز به تست در عمل!!! )

 

نوع شمارشی DftFlags :

توضیح گزینه ها :

  • DFT_INVERSE : اگر این پرچم فعال باشد تبدیل فوریه گسسته معکوس وگرنه تبدیل فوریه گسسته مستقیمِ رو محاسبه میکنه.
  • DFT_SCALE : اگر این پرچم فعال شود، تابع، پس از تبدیل، نتیجه را مقیاس میکند ( آن را بر تعداد عناصر آرایه تقسیم کنید )؛ این پرچم، معمولاً با DFT_INVERSE ترکیب میشود.
  • DFT_ROWS : ؟؟؟؟؟ ( نیاز به تست در عمل!!! )
  • DFT_COMPLEX_OUTPUT :  ؟؟؟؟؟ ( نیاز به تست در عمل!!! )
  • DFT_REAL_OUTPUT : اگر پرچم DFT_INVERSE فعال شده باشد ( که خب یعنی، تبدیل معکوس داریم انجام میدیم )، حالا اگه پرچم DFT_REAL_OUTPUT رو فعال کنید، خروجی یک آرایه 1-کاناله واقعی هستش و اگه رو فعال نکنید، که خب خروجی یک آرایه 2-کاناله ( کانال واقعی + کانال مختلط ) خواهد بود.
  • DFT_COMPLEX_INPUT : ؟؟؟؟؟ ( نیاز به تست در عمل!!! )
2) تابع idft

2) تابع idft : تبدیل فوریه گسسته معکوس یک آرایه 1-بعدی یا 2-بعدی را محاسبه میکند.

کد idft(src, dst, flags) معادل کد dft(src, dst, flags | DFT_INVERSE) هستش، فلذا تو این مطلب کاری به تابع idft ندارم و برا تبدیل فوریه گسسته مستقیم و یا معکوس از تابع dft استفاده میکنیم و مستقیم و یا معکوس بودن تبدیل رو هم به کمک پارامتر flags تعیین میکنیم.

3) تابع getOptimalDFTSize

3) تابع getOptimalDFTSize : اندازه DFT بهینه را برای یک اندازه برداری معین برمی گرداند.

عملکرد DFT یک تابع یکنوا ( monotonic function ) در اندازه برداری نیست؛ بنابراین، هنگامی که هم‌گشت ( convolution ) دو آرایه را محاسبه میکنید یا تحلیل طیفی ( spectral analysis ) یک آرایه را انجام میدهید، معمولا منطقی است که داده های ورودی را با صفر اضافه کنید تا آرایه کمی بزرگتر به دست آید که می تواند بسیار سریعتر از آرایه اصلی تبدیل شود ( عملکرد محاسبات DFT در برخی از اندازه های آرایه بهتر است )؛ آرایه هایی که اندازه آنها توانی از دو است ( 2، 4، 8، 16، 32، ... ) سریعترین پردازش را دارند؛ اگرچه، آرایه هایی که اندازه آنها حاصل ضرب 2، 3 و 5 است ( مثلاً 300 = 5*5*3*2*2 ) نیز کاملاً کارآمد پردازش میشوند.

 

تعریف تابع : اندازه آرایه ورودی رو به این تابع میدید و اندازه بهینه رو تحویل میگیرید؛ که خب یکبار برای اندازه بهینه طول آرایه ورودی و یکبار برای اندازه بهینه عرض آرایه ورودی باید از این تابع استفاده کنید؛ عددی که در خروجی میگیرید بزرگتر یا مساوی با vecsize هستش ( کوچکتر نیست! )

 

توجه : خب حالا که به کمک تابع getOptimalDFTSize، اندازه بهینه برای آرایه ورودی رو رو گرفتیم، چطوری اندازه آرایه ورودی رو تغییر بدیم ( بزرگتر کنیم ) و در پیکسل های جدید، 0 قرار بدیم؟ برای این کار میتونیم از تابع copyMakeBorder استفاده کنیم؛ که قبلا در این مطلب ( آموزش ماژول Operations on arrays در OpenCV ) بهش پرداختم.

 

کد نمونه 1) یه کد اولیه برای استفاده از تابع DFT + استفاده از پرچم DFT_COMPLEX_OUTPUT

کد نمونه :

توضیح کد بالا :

خط 3 : یه تصویری رو در مد GRAYSCALE میخونیم. ( 1-کاناله )

خط 6 و 7 : اندازه بهینه برای آرایه ورودی رو به کمک تابع getOptimalDFTSize پیدا میکنیم ( به توضیحات تابع getOptimalDFTSize در همین مطلب، مراجعه کنید. )

خط 11 : اندازه آرایه ورودی رو به اندازه بهینه تغییر میدیدم و در پیکسل های جدید مقدار 0 قرار میدیم.

خط 14 : آرایه ورودی رو به نوع CV_32F تبدیل میکنیم.

خط 16 تا 22 : نتیجه تبدیل فوریه مختلط است؛ که یعنی برای هر مقدار تصویر، نتیجه دو مقدار تصویر است!؛ علاوه بر این، محدوده دامنه فرکانس بسیار بزرگتر از همتای فضایی آن است؛ بنابراین، ما معمولاً اینها را حداقل در قالب اعشاری ذخیره میکنیم؛ بنابراین تصویر ورودی 1-کاناله خود را به 2-کاناله تبدیل میکنیم تا کانال جدید، مقادیر مختلط را نگه دارد.

خط 25 : تبدیل dft رو انجام میدیم.

خط 28 تا 30 : نتایج یک DFT اعدادی مختلط هستند؛ یک عدد مختلط یک قسمت واقعی ( Re ) و یک قسمت مختلط ( خیالی - Im ) داره ( که هر کدوم در یک کانال، از آرایه خروجی قرار دارند )؛ فلذا اومدیم کانال ها رو استخراج کردیم و بعد مقادیر واقعی و مختلط را به قدر ( magnitude ) تبدیل کردیم؛ فرمول محاسبه magnitude به صورت زیر هستش :

\( M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2} \)

خط 32 و 33 : از مقیاس خطی به مقیاس لگاریتمی میریم تا خروجی ما روی صفحه نمایش، قابل نمایش دادن بشه.

\( mag = \log{(1 + mag)} \)

خط 37 : در خط 11، اندازه آرایه ورودی رو تغییر دادیم، فلذا طول و عرض آرایه ورودی میتونه فرد و یا زوج باشه؛ در ادامه پروژه نیاز داریم که این اندازه عددی زوج باشه فلذا اندازه آرایه رو زوج میکنیم و آرایه رو برش میدیم؛ این & کردن طول و عرض mag با -2، برای زوج کردنشون بکار میره؛ در این مطلب ( زوج یا فرد کردن اعداد در C++‎ ) در این باره صحبت کردم.

خط 40 تا 55 : برای تجسم بهتر، ربع‌های نتیجه را دوباره مرتب می‌کنیم، به طوری که مبدا (0، 0) با مرکز تصویر مطابقت داشته باشد.

خط 58 : نرمالیزه کردن خروجی و بردن داده ها به محدوده 0 تا 1، تا قابل نمایش باشه خروجی.

خط 60 و 61 : نمایش ورودی و خروجی.

نتیجه کد بالا :

خروجی ورودی
آموزش تابع dft در OpenCV آموزش تابع dft در OpenCV

 

توجه 1 : البته برای سادگی میشد کد زیر رو :

به صورت زیر نوشت، تا کدها کوتاه تر بشن :

 

توجه 2 : به تابع dft یه ورودی 2 کاناله دادیم، پرچم ها رو هم تنظیم نکردیم ( که براش چیزی رو تعیین کنیم )، فلذا خروجیش هم 2 کاناله خواهد بود؛ اما خب میخوام کدمو ساده تر کنم، ورودی رو 1 کاناله میدم و خروجی رو 2 کاناله میگیرم! ( برای این کار باید از پارامتر flags کمک میگیرم )؛ تو همچین وضعیتی باید پرچم DFT_COMPLEX_OUTPUT رو استفاده کنم تا تابع dft، خروجی 1 کاناله بهم نده ( چون ورودی 1 کاناله میخوایم بهش بدم، قطعا خروجی 1 کاناله هم بهمون میده! اگه از پرچم DFT_COMPLEX_OUTPUT استفاده نکنم! )؛ فلذا کد زیر :

به صورت زیر ساده سازی میشه : متغییر planes رو هم حذف نکردم چون تو پروژه ازش استفاده کردم!

کد نمونه 2) تبدیل فوریه مستقیم و سپس تبدیل فوریه معکوس! + استفاده از پرچم DFT_INVERSE

یه کد ساده که یه تصویر ورودی میگیره، تبدیل فوریه گسسته مستقیم ( dft ) رو انجام میده و بعد تبدیل فوریه گسسته معکوس ( idft ) رو انجام میده، توقع داریم که تصویر اولیه با تصویر نهایی یکسان باشه، کد زیر رو میتونید تست کنید، نتیجه ورودی و خروجی یکسان هستش؛ با توجه به این که ما اندازه آرایه ورودی رو بهینه میکنیم و تعییر میدیم، فلذا اندازه تصویر ورودی خروجی یکسان نی ( تصویر خروجی یکم بزرگتره ) که خب اگه میخواید هر دو برابر باشن، میتونید اون کدهای بهینه کدرن اندازه آرایه ورودی رو حذف کنید.

 

توجه : هیچ یک از توابع dft و idft به طور پیش فرض نتیجه را مقیاس نمیکنند؛ بنابراین، شما باید DFT_SCALE را به یکی از dft یا idft به صراحت ارسال کنید تا این تبدیل ها به صورت متقابل معکوس شوند؛ وقتی تبدیل dft و بعد idft استفاده میکنیم، اگر از پرچم DFT_SCALE استفاده کرده باشیم، خروجی idft رو میشه نوعشو به CV_8U تبدیل کرد ( به کمک تابع convertTo ) و بعد نمایش داد و یا از تابع normalize استفاده کرد و بعد نمایش داد، تغییر در تصویر ورودی اولیه و نهایی ایجاد نمیشه؛ اما اگه از پرچم DFT_SCALE استفاده نکرده باشیم، خروجی idft رو فقط میشه به تابع normalize داد و خروجی تابع normalize رو نمایش داد ( نمیتونید از تابع convertTo استفاده کنید ).

کد نمونه 3) ویرایش نتیجه تبدیل فوریه؟! + استفاده از پرچم DFT_REAL_OUTPUT

تصویری به کد زیر بدید که مضربی از 2 باشه، و طول و عرضش فرد نباشن، چون قسمت بهینه کردن اندازه آرایه رو حذف کردم تا کد زیر ساده بشه؛ اومدم تبدیل فوریه مستقیم رو حساب کردم، نتیجه magnitude رو ویرایش کردم و بعد تبدیل فوریه معکوس اعمال کردم، اینطوری تصویر اصلی رو ویرایش کردم، یه جورایی انگار خطوط عمودی رو از تصویر ورودی حذف کردم؛ یکم بازی با توابع dft و idft، تا کار باهاش دستتون بیاد.

نتیجه کد بالا :

 

mag before edit src before edit
OpenCV DFT Tutorial OpenCV DFT Tutorial
src after edit mag after edit
OpenCV DFT Tutorial OpenCV DFT Tutorial

 

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

تو تبدیل معکوس، دنبال کانال واقعی هستیم، کاری به کانال مختلط نداریم، فلذا بهتره که از پرچم DFT_REAL_OUTPUT استفاده کنیم، تا آرایه خروجی 1 کاناله باشه ( فقط کانال واقعی؛ اگه خروجی 2 کاناله باشه یعنی هم کانال واقعی رو داریم و هم کانال مختلط رو )؛ فلذا دیگه نیازی به استفاده از تابع split نداریم تا کانال ها رو استخراج کنه برامون!؛ فلذا ساده شده کد بالا، به صورت زیر میشه :

 

توجه 2 : کد 3 رو میشه تغییر داد، اون قسمتی که میایم تصویر mag رو ویرایش میکنیم ( خط 54 تا 61 )

حالت 1) کد زیر رو در پروژه 3 استفاده کردم، همونطور که قبلا گفتم، انگار خطوط عمودی رو از تصویر ورودی حذف میکنه، این کد.

حالت 2) اگر بجای کدهای فوق، از کد زیر استفاده کنیم، میشه تبدیل LPF :

حالت 3) اگه بجای کدهای فوق، از کد زیر استفاده کنیم، میشه تبدیل HPF :

نتیجه حالت 2 و 3 :

mag before edit SRC
OpenCV DFT Tutorial آموزش تابع dft در OpenCV
mag after edit _ HPF  mag after edit - LPF
OpenCV DFT Tutorial OpenCV DFT Tutorial
src after edit _ HPF src after edit - LPF
OpenCV DFT Tutorial OpenCV DFT Tutorial

توجه : اگه در منابع دو کد بالا، میبینید که تصویر HPF تیره هستش و تو این مطلب، تصویر HPF روشن هستش، به این دلیله که اونا کد زیر رو قبل از normalize آرایه src2؛ کد زیر رو استفاده میکنند :

 

منبع حالت های 1 و 2 و 3 :

کد نمونه 4) تشخیص تاری تصاویر به کمک تبدیل فوریه

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

نتیجه کد بالا :

blur3x3.jpg / mean = 6.3738 / The image is blurry! main.jpg / mean = 13.8434 / The image is not blurry!
OpenCV blur detection with Fourier Transform OpenCV blur detection with Fourier Transform
blur10x10.jpg / mean = -9.51184 / The image is blurry! blur5x5.jpg / mean = -3.43964 / The image is blurry!
OpenCV blur detection with Fourier Transform OpenCV blur detection with Fourier Transform

 

توجه : اگه تصاویر تار ( blur ) دم دست ندارید، میتونید برای ایجاد تصاویری blur، به این مطلب ( آموزش ماژول Image Filtering در OpenCV ) مراجعه کنید.

 

مطالعه بیشتر : برا کسایی که دوس دارن در این زمینه مطالعه بیشتری بکنن، میتونید به فایل زیر مراجعه کنید، اطلاعات مفیدی باید داشته باشه!

 

منبع :

 

منابع : 

 

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

 

تعداد مطالب : 367 تا
جنگ ما فتح قدس را به همراه خواهد داشت. [ امام خمینی (ره) ]
بقیه جلسات : آموزش OpenCV
ارسال دیدگاه
0