عملیات اصلی

عملیات پایه‌ای روی تصاویر

۵ دقیقه مطالعهOpenCV 5.xبه‌روزرسانی:

هدف#

یادگیری:

  • دسترسی به مقادیر پیکسل‌ها و اصلاح آن‌ها
  • دسترسی به مشخصات و ویژگی‌های تصویر
  • تعیین ناحیه مورد نظر (Region of Interest یا ROI)
  • تفکیک و ادغام کانال‌های تصویر

تقریباً تمام عملیات این بخش عمدتاً به جای OpenCV با Numpy مرتبط هستند. برای نوشتن کدهای بهینه‌تر با OpenCV، داشتن دانش مناسب از Numpy الزامی است.

(مثال‌ها در یک ترمینال پایتون نشان داده خواهند شد، چرا که بیشتر آن‌ها فقط خطوط تکی کد هستند)

دسترسی و تغییر مقادیر پیکسل‌ها#

بیایید ابتدا یک تصویر رنگی را بارگذاری کنیم:

>>> import numpy as np
>>> import cv2 as cv

>>> img = cv.imread('messi5.jpg')
>>> assert img is not None, "file could not be read, check with os.path.exists()"

می‌توانید با استفاده از مختصات سطر و ستون، به مقدار یک پیکسل دسترسی داشته باشید. برای تصویر BGR، آرایه‌ای از مقادیر آبی، سبز و قرمز (Blue, Green, Red) برمی‌گرداند. برای تصویر در مقیاس خاکستری، صرفاً شدت روشنایی متناظر بازگردانده می‌شود.

>>> px = img[100,100]
>>> print( px )
[157 166 200]

# accessing only blue pixel
>>> blue = img[100,100,0]
>>> print( blue )
157

می‌توانید مقادیر پیکسل را به همان روش تغییر دهید.

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

Warning

کتابخانه Numpy یک کتابخانه بهینه‌شده برای محاسبات سریع روی آرایه‌ها است. بنابراین دسترسی و تغییر تک‌تک پیکسل‌ها به این شیوه بسیار کند خواهد بود و توصیه نمی‌شود.

دسترسی به ویژگی‌های تصویر#

ویژگی‌های تصویر شامل تعداد سطرها، ستون‌ها و کانال‌ها؛ نوع داده تصویر؛ تعداد پیکسل‌ها و غیره است.

ابعاد و شکل (Shape) تصویر از طریق img.shape قابل دسترسی است. این ویژگی یک چندتایی (tuple) شامل تعداد سطرها، ستون‌ها و کانال‌ها (در صورت رنگی بودن تصویر) را برمی‌گرداند:

>>> print( img.shape )
(342, 548, 3)

تعداد کل پیکسل‌ها از طریق img.size قابل دسترسی است:

>>> print( img.size )
562248

نوع داده تصویر توسط `img.dtype` به دست می‌آید:

>>> print( img.dtype )
uint8

ناحیه مورد نظر در تصویر (Image ROI)#

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

ناحیه ROI مجدداً با استفاده از اندیس‌گذاری Numpy به دست می‌آید. در اینجا من توپ را انتخاب کرده و آن را در ناحیه دیگری از تصویر کپی می‌کنم:

>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball

نتایج را در زیر بررسی کنید:

عملیات پایه‌ای روی تصاویر — تصویر ۱

تفکیک و ادغام کانال‌های تصویر#

گاهی اوقات نیاز است که به صورت جداگانه روی کانال‌های B، G و R یک تصویر کار کنید. در این حالت، باید تصویر BGR را به کانال‌های مجزا تفکیک (split) نمایید. در حالات دیگر، ممکن است نیاز باشد این کانال‌های تکی را برای ساخت تصویر BGR با یکدیگر ترکیب (merge) کنید. این کار را می‌توانید به سادگی به صورت زیر انجام دهید:

>>> b,g,r = cv.split(img)
>>> img = cv.merge((b,g,r))

یا

>>> b = img[:,:,0]

فرض کنید می‌خواهید تمام پیکسل‌های قرمز را صفر کنید؛ نیازی نیست ابتدا کانال‌ها را تفکیک کنید، ایندکس‌گذاری Numpy بسیار سریع‌تر است:

>>> img[:,:,2] = 0

Warning

تابع ()cv.split یک عملیات سنگین (از نظر زمان پردازش) است. بنابراین فقط در صورت ضرورت از آن استفاده کنید؛ در غیر این صورت از ایندکس‌گذاری Numpy استفاده نمایید.

ایجاد حاشیه برای تصاویر (Padding)#

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

  • src - تصویر ورودی

  • top، bottom، left، right - پهنای حاشیه بر حسب تعداد پیکسل در جهت‌های متناظر

  • borderType - پرچمی که مشخص می‌کند چه نوع حاشیه‌ای اضافه شود. می‌تواند شامل انواع زیر باشد:

    • cv.BORDER_CONSTANT - یک حاشیه با رنگ ثابت اضافه می‌کند. مقدار رنگ باید به عنوان آرگومان بعدی ارائه شود.
    • cv.BORDER_REFLECT - حاشیه بازتاب آینه‌ای عناصر مرزی خواهد بود، مانند این: fedcba|abcdefgh|hgfedcb
    • cv.BORDER_REFLECT_101 یا cv.BORDER_DEFAULT - مانند مورد فوق، اما با تغییری جزئی، مانند این: gfedcb|abcdefgh|gfedcba
    • cv.BORDER_REPLICATE - آخرین عنصر در سرتاسر حاشیه تکرار می‌شود، مانند این: aaaaaa|abcdefgh|hhhhhhh
    • cv.BORDER_WRAP - قابل توصیف نیست، به این شکل خواهد بود: cdefgh|abcdefgh|abcdefg
  • value - رنگ حاشیه در صورتی که نوع حاشیه cv.BORDER_CONSTANT باشد

در زیر یک نمونه کد برای درک بهتر ارائه شده است که تمامی این انواع حاشیه را نشان می‌دهد:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

BLUE = [255,0,0]

img1 = cv.imread('opencv-logo.png')
assert img1 is not None, "file could not be read, check with os.path.exists()"

replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_CONSTANT,value=BLUE)

plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

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

عملیات پایه‌ای روی تصاویر — تصویر ۲