عکاسی محاسباتی
نوفهزدایی از تصویر
هدف#
در این فصل،
- با الگوریتم نوفهزدایی میانگینهای غیرمحلی (Non-Local Means Denoising) برای حذف نویز در تصویر آشنا خواهید شد.
- توابع مختلفی مانند ()cv.fastNlMeansDenoising، ()cv.fastNlMeansDenoisingColored و غیره را خواهید دید.
تئوری#
در فصول قبل، تکنیکهای زیادی برای هموارسازی تصویر مانند تاری گاوسی (Gaussian Blurring)، تاری میانه (Median Blurring) و غیره را دیدیم که تا حدی در حذف مقادیر کم نویز خوب بودند. در آن تکنیکها، ما یک همسایگی کوچک در اطراف یک پیکسل را در نظر میگرفتیم و عملیاتی مانند میانگین وزندار گاوسی، میانه مقادیر و غیره را برای جایگزینی عنصر مرکزی انجام میدادیم. به طور خلاصه، حذف نویز در یک پیکسل، محلی برای همسایگی آن بود.
نویز دارای ویژگی خاصی است؛ نویز معمولاً یک متغیر تصادفی با میانگین صفر در نظر گرفته میشود. یک پیکسل نویزی را در نظر بگیرید: که در آن مقدار واقعی پیکسل و نویز در آن پیکسل است. شما میتوانید تعداد زیادی از همان پیکسلها (مثلاً تا) را از تصاویر مختلف بگیرید و میانگین آنها را محاسبه کنید. در حالت ایدهآل، از آنجا که میانگین نویز صفر است، باید به برسید.
میتوانید خودتان این موضوع را با یک راهاندازی ساده بررسی کنید. یک دوربین ثابت را برای چند ثانیه در مکانی مشخص نگه دارید. این کار فریمهای متعددی یا تصاویر زیادی از یک صحنه را به شما میدهد. سپس کدی بنویسید تا میانگین تمام فریمهای ویدیو را پیدا کند (این کار اکنون باید برای شما بسیار ساده باشد). نتیجه نهایی و فریم اول را با هم مقایسه کنید؛ کاهش نویز را مشاهده خواهید کرد. متأسفانه این روش ساده در برابر حرکات دوربین و صحنه مقاوم نیست. همچنین اغلب فقط یک تصویر نویزی در دسترس است.
بنابراین ایده ساده است: ما به مجموعهای از تصاویر مشابه نیاز داریم تا نویز را میانگینگیری کنیم. یک پنجره کوچک (مثلاً یک پنجره 5x5) در تصویر در نظر بگیرید. احتمال زیادی وجود دارد که همان وصله (Patch) در جای دیگری از تصویر نیز وجود داشته باشد؛ گاهی اوقات در یک همسایگی کوچک اطراف آن. نظرتان درباره استفاده از این وصلههای مشابه با یکدیگر و یافتن میانگین آنها چیست؟ برای آن پنجره خاص، این کار بسیار عالی است. تصویر نمونه زیر را ببینید:

وصلههای آبی رنگ در تصویر مشابه به نظر میرسند. وصلههای سبز رنگ نیز مشابه به نظر میرسند. بنابراین ما یک پیکسل را میگیریم، یک پنجره کوچک در اطراف آن در نظر میگیریم، پنجرههای مشابه را در تصویر جستجو میکنیم، از تمام آن پنجرهها میانگین میگیریم و پیکسل را با نتیجهای که به دست آوردهایم جایگزین میکنیم. این روش، نوفهزدایی میانگینهای غیرمحلی (Non-Local Means Denoising) نام دارد. این روش در مقایسه با تکنیکهای مات کردنی که قبلاً دیدیم زمان بیشتری میبرد، اما نتیجه آن بسیار عالی است. جزئیات بیشتر و دموی آنلاین را میتوان در اولین پیوند در بخش منابع اضافی یافت.
برای تصاویر رنگی، تصویر به فضای رنگی CIELAB تبدیل میشود و سپس مؤلفههای L و AB را به طور جداگانه نوفهزدایی میکند.
نوفهزدایی تصویر در OpenCV#
کتابخانه OpenCV چهار نگارش مختلف از این تکنیک را ارائه میدهد:
- ()cv.fastNlMeansDenoising - با یک تصویر تککاناله خاکستری (Grayscale) کار میکند
- ()cv.fastNlMeansDenoisingColored - با یک تصویر رنگی کار میکند.
- ()cv.fastNlMeansDenoisingMulti - با توالی تصاویری که در یک بازه زمانی کوتاه ثبت شدهاند کار میکند (تصاویر خاکستری)
- ()cv.fastNlMeansDenoisingColoredMulti - همانند مورد بالا، اما برای تصاویر رنگی.
آرگومانهای مشترک عبارتند از:
- h: پارامتری که قدرت فیلتر را تعیین میکند. مقدار بالاتر h نویز را بهتر حذف میکند، اما جزئیات تصویر را نیز از بین میبرد (مقدار ۱۰ مناسب است).
- hForColorComponents: مانند h است، اما فقط برای تصاویر رنگی (معمولاً برابر با h).
- templateWindowSize: باید فرد باشد (مقدار ۷ توصیه میشود).
- searchWindowSize: باید فرد باشد (مقدار ۲۱ توصیه میشود).
لطفاً برای جزئیات بیشتر در مورد این پارامترها به اولین پیوند در بخش منابع اضافی مراجعه کنید.
ما در اینجا موارد ۲ و ۳ را نشان خواهیم داد؛ مابقی بر عهده شماست.
۱. ()cv.fastNlMeansDenoisingColored#
همانطور که در بالا ذکر شد، برای حذف نویز از تصاویر رنگی استفاده میشود (انتظار میرود نویز گاوسی باشد). مثال زیر را ببینید:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('die.png')
dst = cv.fastNlMeansDenoisingColored(img,None,10,10,7,21)
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst)
plt.show()
در زیر نسخه بزرگنماییشده نتیجه آمده است. تصویر ورودی من نویز گاوسی با دارد. نتیجه را ببینید:

۲. ()cv.fastNlMeansDenoisingMulti#
اکنون همان روش را روی یک ویدیو اعمال خواهیم کرد. آرگومان اول فهرستی از فریمهای نویزی است. آرگومان دوم imgToDenoiseIndex مشخص میکند کدام فریم را باید نوفهزدایی کنیم، برای این منظور اندیس فریم را در فهرست ورودی خود ارسال میکنیم. مورد سوم temporalWindowSize است که تعداد فریمهای مجاور را برای استفاده در نوفهزدایی مشخص میکند که باید فرد باشد. در این حالت، در مجموع temporalWindowSize فریم استفاده میشود که فریم مرکزی همان فریمی است که باید نوفهزدایی شود. برای مثال، شما فهرستی از ۵ فریم را به عنوان ورودی ارسال کردید. فرض کنید imgToDenoiseIndex = 2 و temporalWindowSize = 3 باشد. در این صورت فریمهای ۱، ۲ و ۳ برای نوفهزدایی فریم ۲ استفاده میشوند. بیایید یک مثال را ببینیم:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
cap = cv.VideoCapture('vtest.avi')
# create a list of first 5 frames
img = [cap.read()[1] for i in range(5)]
# convert all to grayscale
gray = [cv.cvtColor(i, cv.COLOR_BGR2GRAY) for i in img]
# convert all to float64
gray = [np.float64(i) for i in gray]
# create a noise of variance 25
noise = np.random.randn(*gray[1].shape)*10
# Add this noise to images
noisy = [i+noise for i in gray]
# Convert back to uint8
noisy = [np.uint8(np.clip(i,0,255)) for i in noisy]
# Denoise 3rd frame considering all the 5 frames
dst = cv.fastNlMeansDenoisingMulti(noisy, 2, 5, None, 4, 7, 35)
plt.subplot(131),plt.imshow(gray[2],'gray')
plt.subplot(132),plt.imshow(noisy[2],'gray')
plt.subplot(133),plt.imshow(dst,'gray')
plt.show()
تصویر زیر نسخه بزرگنماییشده نتیجهای است که به دست آوردهایم:

محاسبات این روش زمان قابل توجهی میبرد. در نتیجه، تصویر اول فریم اصلی، تصویر دوم فریم نویزی و تصویر سوم تصویر نوفهزداییشده است.
منابع اضافی#
- http://www.ipol.im/pub/art/2011/bcm_nlm/ (شامل جزئیات، دموی آنلاین و غیره است. بازدید از آن اکیداً توصیه میشود. تصویر آزمایشی ما از این لینک تولید شده است)
- دوره آنلاین در وبسایت coursera (تصویر اول از اینجا گرفته شده است)