انتقال رنگ بین تصاویر با استفاده از الگوریتم Color Transfer
یکی از ابزارهای پرکاربرد در نرم افزار Phtoshop، انتقال رنگ بین تصاویر است؛ یعنی رنگ های تصویر اول را طبق یک رابطه ریاضی به پیکسل های تصویر دوم منتقل می کند. شاید اولین روشی که برای این کار به ذهن خطور کند استفاده از Hostogram تصاویر باشد. ولی این روش دو مشکل اساسی دارد. اول آنکه که نتیجه مطلوب را بدست نمی دهد و مهم تر آنکه زمان زیادی صرف این روش خواهد شد.
در سال ۲۰۰۱، Reinhard الگوریتم جدیدی در مقاله خود ارائه داد که انتقال رنگ را بر حسب حالت LAB انجام می داد (LAB هم مانند RGB نوعی کانال رنگی محسوب می شود). این الگوریتم سریع تر است و نتیجه مطلوب تری را از خود نشان داده است. از توضیح این الگوریتم صرف نظر می کنم و تنها مراحل آن را به طور خلاصه توضیح خواهم داد:
- مرحله ۱: بارگذاری تصاویر Source و Target. که تصویر Target تصویری است که قرار است رنگ های تصویر Source به آن اعمال شود.
- مرحله ۲: تبدیل BGR به LAB برای هر دو تصویر Source و Target
- مرحله ۳: جدا کردن کانال ها برای هر دو تصویر
- مرحله ۴: مقایسه میانگین و استاندارد انحراف هر کانال LAB برای هر دو تصویر
- مرحله ۵: کم کردن میانگین کانال های LAB تصویر Target از کانال های تصویر Target
- مرحله ۶: محاسبه ابعاد محدوده کانال ها
- مرحله ۷: افزودن میانگین کانال های LAB تصویر Source
- مرحله ۸: حذف کردن مقادیری که خارج از محدوده [۰,۲۵۵] هستند.
- مرحله ۹: ادغام کانال ها
- مرحله ۱۰: تبدبل از حالت LAB به BGR
1 2 3 |
# import the necessary packages import numpy as np import cv2 |
پکیج های Numpy برای Numerical Processing و cv2 برای OpenCV هستند.
1 2 3 4 5 6 |
def color_transfer(source, target): # convert the images from the RGB to L*ab* color space, being # sure to utilizing the floating point data type (note: OpenCV # expects floats to be 32-bit, so use that instead of 64-bit) source = cv2.cvtColor(source, cv2.COLOR_BGR2LAB).astype("float32") target = cv2.cvtColor(target, cv2.COLOR_BGR2LAB).astype("float32") |
برای این الگوریتم تابعی با عنوان color_transfer ایجاد می کنیم. این تابع دارای دو ورودی به نام های Source و Target است که قبلاً در مورد آنها توضیح داده شد.
کار بعدی که باید انجام دهیم تبدیل هر دو تصویر از حالت BGR به LAB است. توجه کنید که به صورت پیش فرض تصاویر در OpenCV به صورت uint8 هستند و لازم است که آنها را برای این الگوریتم به حالت float32 تبدیل نماییم (مرحله ۲).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# compute color statistics for the source and target images (lMeanSrc, lStdSrc, aMeanSrc, aStdSrc, bMeanSrc, bStdSrc) = image_stats(source) (lMeanTar, lStdTar, aMeanTar, aStdTar, bMeanTar, bStdTar) = image_stats(target) # subtract the means from the target image (l, a, b) = cv2.split(target) l -= lMeanTar a -= aMeanTar b -= bMeanTar # scale by the standard deviations l = (lStdTar / lStdSrc) * l a = (aStdTar / aStdSrc) * a b = (bStdTar / bStdSrc) * b # add in the source mean l += lMeanSrc a += aMeanSrc b += bMeanSrc # clip the pixel intensities to [0, 255] if they fall outside # this range l = np.clip(l, 0, 255) a = np.clip(a, 0, 255) b = np.clip(b, 0, 255) |
در این قسمت ابتدا مقدار انحراف میانگین و استاندارد را برای هر تصویر Source و Target توسط تابع image_stats (بعداً توضیح داده خواهد شد) محاسبه می کنیم (مراحل ۳و۴).
سپس مقادیر هر کانال رو از مقدار انحراف میانگین تصویر Target کم می کنیم (مرحله ۵).
پس از آن اندازه محدوده انحراف استاندارد را پیدا می کنیم. این محدوده از ضرب مقدار کانال در تقسیم انحراف استاندارد تصویر Target به انحراف استاندارد تصویر Source بدست می آید (مرحله ۶).
اکون نوبت به افزودن انحراف میانگین تصویر Source رسیده است (مرحله ۷).
در آخر مقادیری که خارج از محدوده [۰,۲۵۵] هستند را حذف می کنیم (مرحله ۸).
1 2 3 4 5 6 7 |
# merge the channels together and convert back to the RGB color # space, being sure to utilize the 8-bit unsigned integer data # type transfer = cv2.merge([l, a, b]) transfer = cv2.cvtColor(transfer.astype("uint8"), cv2.COLOR_LAB2BGR) # return the color transferred image return transfer |
در این مرحله لازم است تا کانال های جدید را با یکدیگر ادغام کنیم تا بتوان آن را به یک تصویر واحد تبدیل کرد (مرحله ۹).
سپس آن را از حالت LAB به BGR تبدیل می کنیم. توجه شود که باید فرمت عکس حاصل را از float32 به uint8 تبدیل نماییم. در پایان تصویر نهایی با عنوان transfer برگردانده می شود (مرحله ۱۰).
1 2 3 4 5 6 7 8 9 |
def image_stats(image): # compute the mean and standard deviation of each channel (l, a, b) = cv2.split(image) (lMean, lStd) = (l.mean(), l.std()) (aMean, aStd) = (a.mean(), a.std()) (bMean, bStd) = (b.mean(), b.std()) # return the color statistics return (lMean, lStd, aMean, aStd, bMean, bStd) |
اکنون تابع image_stats را که پیشتر از آن استفاده کرده بودیم را توضیح خواهیم داد. این تابع تنها یک ورودی دارد و با استفاده از دستور cv2.split، کانال های ان را از هم جدا می کند. سپس مقادیر میانگین و استاندارد را برای هر کانال محاسبه و برمی گرداند.
1 2 3 4 5 6 |
#import Source & Target Images source=cv2.imread('sunset.jpg') target=cv2.imread('sky_01.jpg') transfor=color_transfer(source,target) cv2.imwrite('Color Transfor.jpg',cv2.resize(transfor,(500,300),cv2.INTER_AREA)) cv2.waitKey(0) |
به منظور تست کد، دو تصویر را فراخوانی کرده و با استفاده از تابع color_transfer که ساخته بودیم رنگ تصویر Source را به تصویر Target انتقال می دهیم. برای نمایش راحت تر خروجی، اندازه آن را تغییر دادم (اختیاری است). در شکل زیر تصویر سمت راست target و تصویر سمت چپ Source است.
در پس از اعمال الگوریتم، شکل زیر نتیجه می شود.
تمامی کدها توسط OpenCV 3.0.0 و Python 2.7 نوشته شده اند. در آخر می توانید کد و تصاویر را دانلود نمایید.