├── Media └── Output.mp4 ├── ObjectTrackingBasedOnColor.py ├── README.md └── colorCalibrationforHSV.py /Media/Output.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noel319/Object-Tracking-based-on-Color-/2e2b3a871351aa5257c0ab3a7507e8a88b6f4926/Media/Output.mp4 -------------------------------------------------------------------------------- /ObjectTrackingBasedOnColor.py: -------------------------------------------------------------------------------- 1 | import imutils # for resizeing image 2 | import cv2 # opencv 3 | 4 | orgLower = (0, 255, 96) 5 | orgUpper = (63, 255, 255) 6 | 7 | cam = cv2.VideoCapture(0) # camera init. 8 | 9 | while True: 10 | 11 | _,frame = cam.read() # reading frame from camera 12 | 13 | frame = imutils.resize(frame, width=600) # resizeing a frame 14 | 15 | blur = cv2.GaussianBlur(frame, (11,11), 0) # smoothing 16 | 17 | hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV) # convert bgr 2 hsv color format 18 | 19 | mask = cv2.inRange(hsv, orgLower, orgUpper) # mask the orange color 20 | 21 | mask = cv2.erode(mask, None, iterations = 2) 22 | 23 | mask = cv2.dilate(mask, None, iterations = 2) 24 | 25 | cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2] 26 | 27 | center = None 28 | if len(cnts) > 0: 29 | c = max(cnts, key=cv2.contourArea) 30 | ((x,y), radius) = cv2.minEnclosingCircle(c) 31 | M = cv2.moments(c) 32 | center = (int(M["m10"] / M["m00"]),int(M["m01"] / M["m00"])) 33 | 34 | if radius > 10: 35 | 36 | cv2.circle(frame,(int(x), int(y)),int(radius),(0.255,255), 2) 37 | 38 | cv2.circle(frame, center,5,(0,0,255),-1) 39 | 40 | # print(center,radius) 41 | if radius > 250: 42 | print("stop") 43 | else: 44 | if(center[0]<150): 45 | print("Left") 46 | elif(center[0]>450): 47 | print("Right") 48 | elif(center[0]<250): 49 | print("Front") 50 | else: 51 | print("Stop") 52 | 53 | cv2.imshow("Frame", frame) 54 | key = cv2.waitKey(1) & 0xFF 55 | if key == ord("q"): 56 | break 57 | 58 | cam.release() 59 | cv2.destroyAllWindows() 60 | 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object-Tracking-based-on-Color 2 | 3 | object tracking based on color using HSV (Hue Saturation Value) 4 | 5 | Formulas used here 6 | 7 | * Minimum Enclosing Circle : 8 | 9 | ((x, y), radius) = cv2.minEnclosingCircle( countourArea ) 10 | * Moments to find center of the Area : 11 | 12 | Image moments help you to calculate some features like center of mass of the object, area of the object etc. 13 | 14 | M = cv2.moments(c) 15 | 16 | center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) 17 | 18 | # This Color Based Object Tracking is Based on SELF-DRIVING-CONCEPT 19 | 20 | * If the particular Color is goes long, then it shows "Front" 21 | 22 | * If the particular Color is comes close, then it shows "Stop" 23 | 24 | * If the particular Color is goes at the right corner, then it shows "Right" 25 | 26 | * If the particular Color is goes at the left corner, then it shows "Left" 27 | 28 | # Hue Saturation Value : 29 | 30 | * HSV Color Scale: The HSV (which stands for Hue Saturation Value) scale provides a numerical readout of your image that corresponds to the color names contained therein. Hue is measured in degrees from 0 to 360. For instance, cyan falls between 181–240 degrees, and magenta falls between 301–360 degrees. 31 | 32 | * To run the colorCalibrationforHSV file and get your color values and use it in main code 33 | 34 | # Steps : 35 | 36 | * Reading frame from camera 37 | 38 | * pre-processing image 39 | 40 | * Finding contours 41 | 42 | * Drawing minimum enclosing circle 43 | 44 | * Finding centre of contour Area 45 | 46 | * Drawing circle & centre 47 | 48 | * Direction based on radius & position 49 | 50 | * Run the code and display the output 51 | 52 | # Output : 53 | 54 | * To see the output video, then go to the media file and check it out. 55 | -------------------------------------------------------------------------------- /colorCalibrationforHSV.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from PIL import Image 3 | from PIL import ImageTk 4 | from tkinter import filedialog 5 | import time,cv2 6 | 7 | import numpy as np 8 | from subprocess import check_output 9 | import pyautogui 10 | from threading import Thread, Lock 11 | once = True 12 | img_screenshot = None 13 | cam = cv2.VideoCapture(0) 14 | class App: 15 | original_image = None 16 | hsv_image = None 17 | # switch to make sure screenshot not taken while already pressed 18 | taking_screenshot = False 19 | 20 | def __init__(self, master): 21 | self.img_path = None 22 | frame = tk.Frame(master) 23 | frame.grid() 24 | root.title("Sliders") 25 | 26 | self.hue_lbl = tk.Label(text="Hue", fg='red') 27 | self.hue_lbl.grid(row=2) 28 | 29 | self.low_hue = tk.Scale(master, label='Low',from_=0, to=179, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 30 | self.low_hue.grid(row=3) 31 | 32 | self.high_hue = tk.Scale(master,label='High', from_=0, to=179, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 33 | self.high_hue.set(179) 34 | self.high_hue.grid(row=4) 35 | ########################################################################################################### 36 | self.sat_lbl = tk.Label(text="Saturation", fg='green') 37 | self.sat_lbl.grid(row=5) 38 | 39 | self.low_sat = tk.Scale(master, label='Low',from_=0, to=255, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 40 | self.low_sat.set(100) 41 | self.low_sat.grid(row=6) 42 | 43 | self.high_sat = tk.Scale(master, label="High", from_=0, to=255, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 44 | self.high_sat.set(255) 45 | self.high_sat.grid(row=7) 46 | ########################################################################################################### 47 | self.val_lbl = tk.Label(text="Value", fg='Blue') 48 | self.val_lbl.grid(row=8) 49 | 50 | self.low_val = tk.Scale(master, label="Low",from_=0, to=255, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 51 | self.low_val.set(100) 52 | self.low_val.grid(row=9) 53 | 54 | self.high_val = tk.Scale(master, label="High",from_=0, to=255, length=500,orient=tk.HORIZONTAL, command=self.show_changes) 55 | self.high_val.set(255) 56 | self.high_val.grid(row=10) 57 | 58 | ########################################################################################################### 59 | # buttons 60 | #self.reset_btn = tk.Button(text='Reset', command=self.reset_values) 61 | #self.reset_btn.grid(row=1,column=1) 62 | 63 | self.print_btn = tk.Button(text='Print', command=self.print_values) 64 | self.print_btn.grid(row=2, column=1) 65 | 66 | self.reds = tk.Button(text="Reds", fg='red', command=self.preset_r) 67 | self.reds.grid(row=3, column=1) 68 | 69 | self.reds = tk.Button(text="Greens", fg='green', command=self.preset_g) 70 | self.reds.grid(row=4, column=1) 71 | 72 | self.reds = tk.Button(text="Blues", fg='blue', command=self.preset_b) 73 | self.reds.grid(row=5, column=1) 74 | 75 | # Open 76 | self.open_btn = tk.Button(text="Open", command=self.open_file) 77 | self.open_btn.grid(row=6, column=1) 78 | 79 | # Screenshot 80 | self.screenshot_btn = tk.Button(text="Screenshot", command=self.screenshot_standby) 81 | self.screenshot_btn.grid(row=7, column=1) 82 | # print mask array 83 | #self.print_mask_array_btn = tk.Button(text="Print Array", command=self.print_img_array) 84 | #self.print_mask_array_btn.grid(row=9, column=1) 85 | ########################################################################################################### 86 | # timer label 87 | self.screenshot_timer_lbl = tk.Label(text="Timer", fg='Red') 88 | self.screenshot_timer_lbl.grid(row=8, column=1) 89 | 90 | ########################################################################################################## Images 91 | # images 92 | self.hsv_img_lbl = tk.Label(text="HSV", image=None) 93 | self.hsv_img_lbl.grid(row=0, column=0) 94 | 95 | self.original_img_lbl = tk.Label(text='Original',image=None) 96 | self.original_img_lbl.grid(row=0, column=1) 97 | ########################################################################################################## 98 | def open_file(self): 99 | global once 100 | once = True 101 | #img_file = filedialog.askopenfilename() 102 | img_file = "input.jpg" 103 | print(img_file) 104 | # this makes sure you select a file 105 | # otherwise program crashes if not 106 | if img_file != '': 107 | self.img_path = img_file 108 | # this just makes sure the image shows up after opening it 109 | self.low_hue.set(self.low_hue.get()+1) 110 | self.low_hue.set(self.low_hue.get()-1) 111 | else: 112 | print('picked nothing') 113 | return 0 114 | 115 | def preset_r(self, *args): 116 | self.low_hue.set(0) 117 | self.high_hue.set(13) 118 | 119 | self.low_sat.set(100) 120 | self.high_sat.set(255) 121 | 122 | self.low_val.set(50) 123 | self.high_val.set(255) 124 | 125 | def preset_g(self, *args): 126 | self.low_hue.set(36) 127 | self.high_hue.set(90) 128 | 129 | self.low_sat.set(100) 130 | self.high_sat.set(255) 131 | 132 | self.low_val.set(50) 133 | self.high_val.set(255) 134 | 135 | def preset_b(self, *args): 136 | self.low_hue.set(80) 137 | self.high_hue.set(125) 138 | 139 | self.low_sat.set(100) 140 | self.high_sat.set(255) 141 | 142 | self.low_val.set(75) 143 | self.high_val.set(255) 144 | 145 | def show_changes(self, *args): 146 | global once, img_screenshot 147 | if self.img_path == None: 148 | return 0 149 | # gets the values from the sliders 150 | # low blue, green, red 151 | low_hue = self.low_hue.get() 152 | low_sat = self.low_sat.get() 153 | low_val = self.low_val.get() 154 | # gets upper values from sliders 155 | high_hue = self.high_hue.get() 156 | high_sat = self.high_sat.get() 157 | high_val = self.high_val.get() 158 | # does nothing if low values go higher than high values 159 | if low_val > high_val or low_sat > high_sat or low_hue > high_hue: 160 | return 0 161 | 162 | # Sets the original image once, manipulates the copy in next iterations 163 | if once: 164 | # gets image from file 165 | if self.img_path != 'screenshot': 166 | #img_path = 'objects.png' 167 | # loaded as BGR 168 | s, im = cam.read() 169 | self.original_image = im 170 | # image resized 171 | self.original_image = self.resize_image(self.original_image) 172 | self.hsv_image = self.original_image.copy() 173 | #converts image to HSV 174 | self.hsv_image = cv2.cvtColor(self.hsv_image, cv2.COLOR_BGR2HSV) 175 | 176 | # gets screenshot 177 | else: 178 | self.original_image = img_screenshot 179 | self.hsv_image = img_screenshot.copy() 180 | #converts image to HSV 181 | self.hsv_image = cv2.cvtColor(self.hsv_image, cv2.COLOR_BGR2HSV) 182 | 183 | # OpenCV represetns images in BGR order; however PIL represents 184 | # images in RGB order, so we need to swap the channels 185 | self.original_image = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB) 186 | 187 | # convert the images to PIL format 188 | self.original_image = Image.fromarray(self.original_image) 189 | # convert to ImageTk format 190 | self.original_image = ImageTk.PhotoImage(self.original_image) 191 | # update the original image label 192 | self.original_img_lbl.configure(image=self.original_image) 193 | # Keeping a reference! b/ need to! 194 | self.original_img_lbl.image = self.original_image 195 | once = True 196 | 197 | 198 | 199 | 200 | # sets the lower and uppper values for the mask 201 | # define range of colors in HSV (hue up to 179, sat-255, value-255 202 | lower_color = np.array([low_hue,low_sat,low_val]) 203 | upper_color= np.array([high_hue,high_sat,high_val]) 204 | # red - 0,255,255 (low (hue-10,100,100) high(hue+10,255,255) 205 | # green 60,255,255 206 | # blue -120,255,255 207 | 208 | #creates the mask and result 209 | mask = cv2.inRange(self.hsv_image, lower_color, upper_color) 210 | #res = cv2.bitwise_and(img, img, mask=mask) 211 | 212 | # converting to RGB format 213 | #maskbgr = cv2.cvtColor(mask, cv2.COLOR_HSV2BGR) 214 | #maskrgb = cv2.cvtColor(maskbgr, cv2.COLOR_BGR2RGB) 215 | # converting to PIL format 216 | mask = Image.fromarray(mask) 217 | # convertint to ImageTk format 218 | mask = ImageTk.PhotoImage(mask) 219 | # setting the hsv image to tk image label 220 | self.hsv_img_lbl.configure(image=mask) 221 | # adding a reference to the image to Prevent python's garbage collection from deleting it 222 | self.hsv_img_lbl.image = mask 223 | 224 | def reset_values(self,*args): 225 | self.low_hue.set(0) 226 | self.low_sat.set(100) 227 | self.low_val.set(100) 228 | 229 | self.high_hue.set(179) 230 | self.high_sat.set(255) 231 | self.high_val.set(255) 232 | 233 | def print_values(self,*args): 234 | """Does NOT actually save, just prints, for now""" 235 | print("Low = [{},{},{}]".format(self.low_hue.get(), self.low_sat.get(), self.low_val.get())) 236 | print("High= [{},{},{}]".format(self.high_hue.get(), self.high_sat.get(), self.high_val.get())) 237 | 238 | def screenshot_standby(self,*args): 239 | if not self.taking_screenshot: 240 | take_screenshot_thread = Thread(target=self.take_screenshot) 241 | take_screenshot_thread.start() 242 | else: 243 | return 0 244 | 245 | def take_screenshot(self,*args): 246 | global img_screenshot, once 247 | # switch to stop screenshot button from snaping a shot while snapping a shot 248 | self.taking_screenshot = True 249 | 250 | # switch to always display the screenshot as original everytime 251 | once = True 252 | 253 | # makes sure method 'show_changes' takes screenshot instead of img file 254 | self.img_path = 'screenshot' 255 | 256 | # starts a cound down timer of 3 seconds, parallel to the for loop 257 | screenshot_timer_thread = Thread(target=self.screenshot_timer_lbl_update) 258 | screenshot_timer_thread.start() 259 | for i in range(2): 260 | for _ in range(3): 261 | time.sleep(1) 262 | try: 263 | # sets the first point of screenshot 264 | if i == 0: 265 | x1,y1 = pyautogui.position() 266 | # sets the second point of screenshot 267 | else: 268 | x2,y2 = pyautogui.position() 269 | 270 | except Exception as e: 271 | print("ERROR: {}".format(e)) 272 | print("{}{} {}{}\n".format(x1,y1,x2,y2)) 273 | continue 274 | # exits if width and height are not greater than 0 275 | if x2 - x1 < 1 or y2 - y1 < 1: 276 | print("Retake Screenshot") 277 | print("Width={} Height={}".format(x2 - x1, y2 - y1)) 278 | return 279 | # screenshot taken here with the grabbed coordinates 280 | try: 281 | # top-leftpt, w & h 282 | 283 | screenshoted_image = pyautogui.screenshot(region=(x1,y1,x2-x1,y2-y1)) 284 | screenshoted_image = np.array(screenshoted_image) 285 | except Exception as e: 286 | print(e) 287 | print("Could not capture image") 288 | print("...coords passed pt1({},{}) pt2({},{})".format(x1,y1,x2,y2)) 289 | return 290 | # converts the PIL image format to opencv2 image format 291 | img_screenshot = cv2.cvtColor(screenshoted_image, cv2.COLOR_RGB2BGR) 292 | # printing image array, by taking another screenshot and processing, effects will now show 293 | try: 294 | if args[0] == 'array': 295 | self.taking_screenshot = False 296 | return img_screenshot 297 | except: 298 | pass 299 | 300 | # resizes image if higher than 300px in width or height 301 | img_screenshot = self.resize_image(img_screenshot) 302 | 303 | # this just makes sure the image shows up after opening it 304 | self.low_hue.set(self.low_hue.get()+1) 305 | self.low_hue.set(self.low_hue.get()-1) 306 | # switch to allow for next screenshot 307 | self.taking_screenshot = False 308 | 309 | def screenshot_timer_lbl_update(self,*args): 310 | for _ in range(2): 311 | for i in range(3): 312 | self.screenshot_timer_lbl.config(text="{}".format(i+1)) 313 | time.sleep(1) 314 | self.screenshot_timer_lbl.config(text="{}".format(" ")) 315 | 316 | def resize_image(self,img,*args): 317 | # unpacks width, height 318 | height, width,_ = img.shape 319 | print("Original size: {} {}".format(width, height)) 320 | count_times_resized = 0 321 | while width > 500 or height > 500: 322 | #if width > 300 or height > 300: 323 | # divides images WxH by half 324 | width = width / 2 325 | height = height /2 326 | count_times_resized += 1 327 | # prints x times resized to console 328 | if count_times_resized != 0: 329 | print("Resized {}x smaller, to: {} {}".format(count_times_resized*2,width, height)) 330 | # makes sures image is not TOO small 331 | if width < 300 and height < 300: 332 | width = width * 2 333 | height = height * 2 334 | 335 | img = cv2.resize(img,(int(width),int(height))) 336 | 337 | return img 338 | 339 | def print_img_array(self): 340 | img = self.take_screenshot('array') 341 | #converts image to HSV 342 | img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 343 | # gets the values from the sliders 344 | low_hue = self.low_hue.get() 345 | low_sat = self.low_sat.get() 346 | low_val = self.low_val.get() 347 | # gets upper values from sliders 348 | high_hue = self.high_hue.get() 349 | high_sat = self.high_sat.get() 350 | high_val = self.high_val.get() 351 | lower_color = np.array([low_hue,low_sat,low_val]) 352 | upper_color= np.array([high_hue,high_sat,high_val]) 353 | #creates the mask and result 354 | mask = cv2.inRange(self.hsv_image, lower_color, upper_color) 355 | mask = np.array(mask) 356 | mask.view 357 | 358 | 359 | # Instance of Tkinter 360 | root = tk.Tk() 361 | # New tkinter instnace of app 362 | app = App(root) 363 | # loops over to keep window active 364 | root.mainloop() 365 | --------------------------------------------------------------------------------