توی این پست ادامه مبحث مربوط به thresholding با استفاده از تابع inRange و تابع HSV توضیح میدم .
خب اول از همه یه توضیحی در مورد فضای رنگی HSV میدم که چیه و کلا به چه کاری میاد .
فضای رنگی HSV که مخفف (Hue Saturation Value ) هست یه مدل برای مشخص کردن وضعیت رنگی یک تصویر یا پیکسل توی فضا رنگی هست و شباهت زیادی به مدل RGB داره .
همونطور که میدونید توی مدل RGB ما اگه هر یک از رنگ های آبی ٬ قرمز و سبز یک محور بردار در نظر بگیریم ٬ میتونیم توی یک فضای سه بعدی تمام رنگ هارو شبیه سازی کنیم مثلا مختصات (255,255,255) رنگ سفید بهمون میده یا (66,245,167) رنگ آبی فیروزه ای رو درست می کنه حالا توی HSV هم همینطوره منتهی به یه شکلی دیگه ای نشون میدن .
توی مدل رنگی HSV به جای اینکه به سه تا مولفه x,y,z داشته باشیم که یک فضای مکعبی بسازه ٬ یه فضای استوانه ای داریم که هر مولفشو جدا توضیح میدم .
این مولفه که از بازه صفر تا ۳۶۰ هست رنگ پیکسل رو مشخص می کنه مثلا اگه پیکسل قرمز باشه مقدارش حدودا ۳۲۰ هست و اگه آبی باشه حدودا ۱۶۰ هست . پس کل طیف های رنگی روی ۳۶۰ درجه پخش شدند .
این مولفه از بازه صفر تا 255 هست و تعیین کننده میزان پر رنگی یا کم رنگی پیکسل هست . اگه مقدارش صفر باشه پیسکل سفیده و اگه ۲۵۵ باشه پررنگ ترین رنگ اون زاویه رو نشون میده
این مولفه هم از بازه صفر تا 255 قرار داره و میزان تیرگی یا روشنی پیکسل رو مشخص می کنه اگه یادتون باشه توی پست قبلی ما تصویر های که روشون آستانه تعیین می کردیم همه توی کانال خاکستری بودند مقداری که این مولفه داره دقیقا همون مقداری هست که مولفه y توی پست قبلی داشت یعنی اگه اگه صفر باشه سیاهه و ۲۵۵ هم باشه سفید یا روشنترین حالت پیکسل رو نشون میده .
مدل های رنگی دیگه ای هم مثل HSL هستند که استفاده میشند اما فعلا به همین مدل اکتفا می کنیم .
خب بریم سراغ کد !
import cv2 as cv max_value = 255 max_value_H = 360 // 2 low_H = 0 low_S = 0 low_V = 0 high_H = max_value_H high_S = max_value high_V = max_value window_capture_name = 'Video Capture' window_detection_name = 'Object Detection' low_H_name = 'Low H' low_S_name = 'Low S' low_V_name = 'Low V' high_H_name = 'High H' high_S_name = 'High S' high_V_name = 'High V'
مثل همیشه با نام و یاد خدا کتابخونه رو اد میکنیم و یه سری مقادیر تعیین می کنیم که توی تعیین پارامتر های توابع به کار میاند .
cap = cv.VideoCapture(2) cv.namedWindow(window_capture_name) cv.namedWindow(window_detection_name) cv.createTrackbar(low_H_name, window_detection_name, low_H, max_value_H, on_low_H_thresh_trackbar) cv.createTrackbar(high_H_name, window_detection_name, high_H, max_value_H, on_high_H_thresh_trackbar) cv.createTrackbar(low_S_name, window_detection_name, low_S, max_value, on_low_S_thresh_trackbar) cv.createTrackbar(high_S_name, window_detection_name, high_S, max_value, on_high_S_thresh_trackbar) cv.createTrackbar(low_V_name, window_detection_name, low_V, max_value, on_low_V_thresh_trackbar) cv.createTrackbar(high_V_name, window_detection_name, high_V, max_value, on_high_V_thresh_trackbar)
خب قدم بعدی دادن دسترسی به وبکمه اگه هم از ویدیو یا تصویر از قبل استفاده می کنید مثل پست های قبلی آدرس اون فیلم رو داخل تابع بزارید .
بعد ما میایم دوتا پنجره تعریف می کنیم یکی برای نشون دادن تصویر اورجینال وبکم و یکی هم برای نشون دادن خروجی تابع inRange
بعدشم که یه سری بار مثل قبل برای تعیین پارامتر در لحظه ایجاد می کنیم .
def on_low_H_thresh_trackbar(val): global low_H global high_H low_H = val low_H = min(high_H - 1, low_H) cv.setTrackbarPos(low_H_name, window_detection_name, low_H) def on_high_H_thresh_trackbar(val): global low_H global high_H high_H = val high_H = max(high_H, low_H + 1) cv.setTrackbarPos(high_H_name, window_detection_name, high_H) def on_low_S_thresh_trackbar(val): global low_S global high_S low_S = val low_S = min(high_S - 1, low_S) cv.setTrackbarPos(low_S_name, window_detection_name, low_S) def on_high_S_thresh_trackbar(val): global low_S global high_S high_S = val high_S = max(high_S, low_S + 1) cv.setTrackbarPos(high_S_name, window_detection_name, high_S) def on_low_V_thresh_trackbar(val): global low_V global high_V low_V = val low_V = min(high_V - 1, low_V) cv.setTrackbarPos(low_V_name, window_detection_name, low_V) def on_high_V_thresh_trackbar(val): global low_V global high_V high_V = val high_V = max(high_V, low_V + 1) cv.setTrackbarPos(high_V_name, window_detection_name, high_V)
قدم بعدی که در واقع قبل از گرفتن ورودی وبکم انجام میشه ساختن توابع مربوط به بار هایی هست که ساختیم و کارش اینه که مقداری که هر بار داره رو داخل متغییر هایی که اول کار تعریف کردیم بریزه .
while True: ret, frame = cap.read() if frame is None: break frame_HSV = cv.cvtColor(frame, cv.COLOR_BGR2HSV) frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V)) cv.imshow(window_capture_name, frame) cv.imshow(window_detection_name, frame_threshold) key = cv.waitKey(30) if key == ord('q'): break
قدم بعدی هم گرفتن هر فریم وبکم ٬ تغییر اون از مدل BGR به HSV و گذاشتن اون توی تابع inRange هست. یه توضیحی که بخوام در مورد این تابع بدم اینه که خروجیش دقیقا مثل تابع آستانه باینری هست یعنی هر پیکسلی که مقادیر HSV اون از آستانه ای که ما تعیین کردیم بیشتر باشه سفید نشون میده و هر پیکسلی که کمتر باشه سیاه نشون میده.
خب حالا خروجی که ما میگیریم یه همچین چیزی میشه :
دوتا ویندو داریم که یکی تصویر اورجینال وبکم
و اون یکی خروجی تابع inRange رو با توجه به پارامتر هایی که تعیین کردیم بهمون میده
حالا اصن به چه دردی میخوره !
باز تو مباحث مربوط به تشخیص اشیا به کمک رنگ این تابع و شناخت داشتن به اون میتونه خیلی کمک بکنه
یعنی میشه خیلی راحت به کمک این توابع آستانه ای برای تصویر تعیین کرد که صرفا جسم مورد نظر مارو خروجی بده به عنوان مثال با همین تصویر ورودیمون میتونیم پارامتر ها رو جوری تنظیم کنیم که فقط رنگ قرمز رو تشخیص بده :
یا مثلا یه جسم دیگه :
همونطور که میبینید ما تو خروجی یکم نویز داریم و همونطور که قبلا گفتم میشه با یه تابعی مثلا erode این نویز هارو حذف کرد .
نسخه نویز دار:
نسخه تحلیل رفته :
خب ! اینم از این پست امیدوارم که مطلب مفیدی بوده باشه سورس کدم همینجا میزارم که خودتون اگه دوست داشتید تست کنید .
import cv2 as cv max_value = 255 max_value_H = 360 // 2 low_H = 0 low_S = 0 low_V = 0 high_H = max_value_H high_S = max_value high_V = max_value window_capture_name = 'Video Capture' window_detection_name = 'Object Detection' low_H_name = 'Low H' low_S_name = 'Low S' low_V_name = 'Low V' high_H_name = 'High H' high_S_name = 'High S' high_V_name = 'High V' def on_low_H_thresh_trackbar(val): global low_H global high_H low_H = val low_H = min(high_H - 1, low_H) cv.setTrackbarPos(low_H_name, window_detection_name, low_H) def on_high_H_thresh_trackbar(val): global low_H global high_H high_H = val high_H = max(high_H, low_H + 1) cv.setTrackbarPos(high_H_name, window_detection_name, high_H) def on_low_S_thresh_trackbar(val): global low_S global high_S low_S = val low_S = min(high_S - 1, low_S) cv.setTrackbarPos(low_S_name, window_detection_name, low_S) def on_high_S_thresh_trackbar(val): global low_S global high_S high_S = val high_S = max(high_S, low_S + 1) cv.setTrackbarPos(high_S_name, window_detection_name, high_S) def on_low_V_thresh_trackbar(val): global low_V global high_V low_V = val low_V = min(high_V - 1, low_V) cv.setTrackbarPos(low_V_name, window_detection_name, low_V) def on_high_V_thresh_trackbar(val): global low_V global high_V high_V = val high_V = max(high_V, low_V + 1) cv.setTrackbarPos(high_V_name, window_detection_name, high_V) cap = cv.VideoCapture(2) cv.namedWindow(window_capture_name) cv.namedWindow(window_detection_name) cv.createTrackbar(low_H_name, window_detection_name, low_H, max_value_H, on_low_H_thresh_trackbar) cv.createTrackbar(high_H_name, window_detection_name, high_H, max_value_H, on_high_H_thresh_trackbar) cv.createTrackbar(low_S_name, window_detection_name, low_S, max_value, on_low_S_thresh_trackbar) cv.createTrackbar(high_S_name, window_detection_name, high_S, max_value, on_high_S_thresh_trackbar) cv.createTrackbar(low_V_name, window_detection_name, low_V, max_value, on_low_V_thresh_trackbar) cv.createTrackbar(high_V_name, window_detection_name, high_V, max_value, on_high_V_thresh_trackbar) while True: ret, frame = cap.read() if frame is None: break frame_HSV = cv.cvtColor(frame, cv.COLOR_BGR2HSV) frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V)) cv.imshow(window_capture_name, frame) cv.imshow(window_detection_name, frame_threshold) frame_erode = cv.erode(frame_threshold, None, iterations=4) cv.imshow("new window", frame_erode) cv.imwrite("res.jpg", frame_threshold) cv.imwrite("res2.jpg", frame) cv.imwrite("res3.jpg", frame_erode) key = cv.waitKey(30) if key == ord('q') or key == 27: break