Warning: file_exists(): open_basedir restriction in effect. File(/bin/bash) is not within the allowed path(s): (/home/patriyt191/domains/robotmc.be/:/home/patriyt191/domains/include/:/etc/pki/tls/certs:/tmp:/var/tmp:/usr/local/lib/php/:/usr/local/php81/lib/php/:/usr/local/php0/lib/php/) in /home/patriyt191/domains/robotmc.be/public_html/wiki/includes/shell/CommandFactory.php on line 119
Raspberry Pi3 + OpenCV = Beeldherkenning - RobotMC.be

Raspberry Pi3 + OpenCV = Beeldherkenning

Uit RobotMC.be
Ga naar: navigatie, zoeken

PI3 OpenCV1.jpg

OpenCV installeren

OpenCV installeren op de Pi is wel wat omslachtiger als we gewoon : je moet de brondbestanden importeren en dan moet OpenCV gecompileerd worden op de Pi. Ik heb de volgende site stap per stap gevolgd met goed resultaat :

Install opencv 3 python on your raspberry-pi

  • Let op, het compileren kan enkele uren tijd in beslag nemen, je terminal sessie mag niet onderbroken worden tijdens de compilatie ! Als je dus met "putty" werkt, moet je een "timeout" vermijden. Ikzelf heb maar een scherm en toetsenbord aangesloten en rechtstreeks op de pi gewerkt.
  • OpenCV en Python worden hier in een "virtuele omgeving" geinstalleerd. Dit betekent dat deze installatie los staat van de standaard installatie van Python. Ook alle libraries die je hier wil gebruiken moeten in deze omgeving geinstalleerd worden. Dit doe je niet met "apt get", maar met "pip" ! Eerst zorgen dat je in deze virtuele omgeving werkt, en daarna de nodige librarys installeren.
  • De "virtuele omgeving" is niet direct zichtbaar in je file manager (vb via FTP), omdat deze files en directories "hidden" zijn op de sd kaart. Je kan deze echter zichtbaar maken via "Preferences", "Panels" en dan de optie "Show hidden files"
  • Omschakelen naar de virtuele omgeving "cv" doe je normaal met  : "workon cv". Als dit niet werkt (vanuit de GUI lx terminal lukte dit niet) kan je volgend comando gebruiken : source ~/.virtualenvs/cv/bin/activate. Aan de "prompt" is steeds te merken dat je in de virtuele omgeving werkt : vooraan komt deze dir dan tussen haakjes te staan. Vb virtuele omgeving = cv, dan moet je "prompt er zo uitzien :
(cv) pi@raspberrypi:

Mijn OpenCV programma

Ook hier heb ik als basis het programma van pyimage genomen : track object movement

Ik heb wel enkele wijzigingen aangebracht :

  • Om makkelijk een object te kunnen selecteren kan je met de muis hierop klikken, de kleur van deze pixel wordt dan als referentie genomen. Hiermee worden dan de instelbare grenzen van "hue", "saturation" en "value" bepaald.
  • De positie van het object moet uiteraard worden doorgegeven naar mijn robot. Hiervoor heb ik de seriele poort gekozen. Maar, bij de Pi3 is deze poort standaard naar de bluetooth adapter geleid ! Je moet dus eerst nog een configuratiefile wijzigen om de seriele poort terug op pin 14 en 15 van de GPIO te leiden ! In de file "boot/config.txt" moet je eenvoudigweg "enable uart=1" aanpassen (was enable uart=0).
  • Het tweede probleem was het ontbreken van de "serial" library in de virtuele python omgeving. Dit kan ook eenvoudig opgelost worden : zorg dat je eerst in de virtuele omgeving werkt, en dan "python -m pip install pyserial" zorgt ervoor dat de lib geinstalleerd wordt.
  • De frame rate wordt ook telkens berekend en weergegeven in de terminal.

De camera

Een gewone usb webcam volstaat hier. Ik had wel grote verschillen in framerate naargelang de usb camera : een erg goedkope webcam kwam niet verder dan 4 fps, mijn gitup action cam had ca 6 fps bij 640*480 resolutie, en zelfs 20 fps bij 320*240 resolutie ! De pi-camera zelf heb ik nog niet kunnen testen. De action-cam heeft een zeer grote openingshoek, je hebt dus een groot beeldveld.

Het resultaat

PI3 OpenCV2.jpg

Je start het programma vanuit de terminal : python ball_3.py. Op dat moment moet er wel een "display" geopend zijn op de pi, en er moet een usb webcam aangesloten zijn. Er worden dan 2 schermen vertoond op het display, één met het webcam beeld en een met het resultaat van de kleurfilter. Bovenaan heb je ook 3 trackbars om de grootte van het filter in te stellen. Via de seriele poort wordt er bij elk frame steeds de positie van het grootste gevonden object in pixels doorgegeven. Je kiest een andere kleur door met de muis op de gewenste kleur / object te klikken.

Het python programma vind je hier :

 # in terminal, start vncserver first : 
 # vncserver :1 -geometry 1920x1080
 # export DISPLAY=":1" for open window in  VNC !!!
 # to stop vncserver : vncserver -kill :1
 # to run without terrminal : 
 # nohup python ball_3.py &
 # switch to virtual enviroment :
 # workon cv
 # indien workon = "not found" dan : source ~/.virtualenvs/cv/bin/activate
   
 # import the necessary packages 
  
 import serial
 from collections import deque
 import numpy as np
 import argparse
 import imutils
 import cv2
 import time
 import pickle
 
 def nothing(x):
    pass
 GREEN=(0,255,0)
 BLUE=(255, 0, 0)
 font = cv2.FONT_HERSHEY_PLAIN
 
 # construct the argument parse and parse the arguments
 ap = argparse.ArgumentParser()
 ap.add_argument("-v", "--video", help="path to the (optional) video file")
 ap.add_argument("-b", "--buffer", type=int, default=64, help="max buffer size")
 ap.add_argument("-n", "--show", type=bool, default=0, help="no display")
 args = vars(ap.parse_args())
 
 fps = 0
 Lower = (0, 0, 0)
 Upper = (1, 100, 100)
 hue=0
 sat=0
 val=0
 cam_res_x=640
 cam_res_y=480
 def update(hue,sat,val,hue_width,sat_width,val_width):
    hue_l=hue-hue_width/2
    if(hue_l<=0):hue_l=0
    hue_h= hue_l+hue_width
    red = hue_h - 180
    if(red<0):red=0
    if(hue_h>=180): hue_h=180
    sat_l=sat-sat_width/2
    if(sat_l<=0):sat_l=0
    sat_h=sat_l+sat_width
    if(sat_h>=255):sat_h=255
    val_l=val-val_width/2
    if(val_l<=0):val_l=0
    val_h=val_l+val_width
    if(val_h>=255):val_h=255
    global Lower
    global Upper
    Lower = (hue_l, sat_l, val_l)
    Upper = (hue_h, sat_h, val_h)
       
 # Getting back the objects:
 with open('objs.pkl') as f:  # Python 3: open(..., 'rb')
    disk = pickle.load(f)
    hue=disk['Hue'] 
    sat=disk['Sat'] 
    val=disk['Val']
    hue_width=disk['W_hue']
    sat_width=disk['W_sat']
    val_width=disk['W_val']
    update(hue,sat,val,hue_width,sat_width,val_width)
    print disk
  colors = []
 
 def on_mouse_click (event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        global colors
        global hue,sat,val
        del colors[:]
        #colors=[(y,x)]
        colors.append(frame[y,x].tolist())
        print colors
        color=([y,x])
        last = (len(colors) - 1)
        pixel=  cv2.cvtColor(np.uint8([colors]), cv2.COLOR_BGR2HSV)
        hue=(pixel[0,0,0])
        sat=(pixel[0,0,1])
        val=(pixel[0,0,2])
        update(hue,sat,val,hue_width,sat_width,val_width)
        disk= {'Hue':75, 'Sat':98, 'Val':80, 'W_hue':50,'W_sat':50,'W_val':50}
        disk['Hue'] = hue
        disk['Sat'] = sat
        disk['Val'] = val
        disk['W_hue'] = hue_width
        disk['W_sat'] = sat_width
        disk['W_val'] = val_width
        print disk
        # Saving the objects tot file:
        with open('objs.pkl', 'w') as f:  # Python 3: open(..., 'wb')
            pickle.dump(disk, f)
            
 pts = deque(maxlen=args["buffer"])
  
 # if a video path was not supplied, grab the reference
 # to the webcam
 if not args.get("video", False):
    camera = cv2.VideoCapture(0)
 
 # otherwise, grab a reference to the video file
 else:
    camera = cv2.VideoCapture(args["video"])
 # Reduce the size of video to 320x240 so rpi can process faster	
 camera.set(3,cam_res_x)
 camera.set (4,cam_res_y)	
 start = time.time()
 ser = serial.Serial('/dev/serial0', 38400, timeout=1)
 ser.write("testing")
 
 cv2.namedWindow('Mask')
 cv2.moveWindow('Mask',660,360)
 
 cv2.namedWindow('Frame')
 cv2.moveWindow('Frame',20,200)
 cv2.setMouseCallback('Frame', on_mouse_click)
  # callback if trackbar changes
  def on_trackbar_hue(hue_width):
    update(hue,sat,val,hue_width,sat_width,val_width)
 def on_trackbar_val(val_width):
    update(hue,sat,val,hue_width,sat_width,val_width)
 def on_trackbar_sat(sat_width):
    update(hue,sat,val,hue_width,sat_width,val_width)    
 # create trackbars for color change
 cv2.createTrackbar('Hue_width','Frame',hue_width,50,on_trackbar_hue)
 cv2.createTrackbar('Sat_width','Frame',sat_width,100,on_trackbar_sat)
 cv2.createTrackbar('Val_width','Frame',val_width,100,on_trackbar_val)
   
 while (1):        
 	# grab the current frame
 	(grabbed, frame) = camera.read()
        #frame = cv2.VideoCapture(0)
 	# if we are viewing a video and we did not grab a frame,
 	# then we have reached the end of the video
 	if args.get("video") and not grabbed:
 		break
        # get current positions of three trackbars
        hue_width = cv2.getTrackbarPos('Hue_width','Frame')
        sat_width = cv2.getTrackbarPos('Sat_width','Frame')
        val_width = cv2.getTrackbarPos('Val_width','Frame')
        
 	# resize the frame, blur it, and convert it to the HSV
 	# color space
 	# frame = imutils.resize(frame, width=300)
 	# blurred = cv2.GaussianBlur(frame, (11, 11), 0)
 	hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
 	if colors:
            cv2.putText(hsv, str(colors[-1]), (10, 50), cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 0), 2)
        #cv2.imshow('HSV-colors', hsv)
       
 	# construct a mask for the color between "Lower" and "Upper", then perform
 	# a series of  and erosions to remove any small
 	# blobs left in the mask
 	mask = cv2.inRange(hsv, Lower, Upper)
 	# mask2 = cv2.inRange(hsv, red_0, red_max)
 	# mask = mask1|mask2
 	mask = cv2.erode(mask, None, iterations=2)
 	mask = cv2.dilate(mask, None, iterations=2)
 	# find contours in the mask and initialize the current
 	# (x, y) center of the ball
 	cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
 		cv2.CHAIN_APPROX_SIMPLE)[-2]
 	center = None
 
 	# only proceed if at least one contour was found
 	if len(cnts) > 0:
 		# find the largest contour in the mask, then use
 		# it to compute the minimum enclosing circle and
 		# centroid
 		c = max(cnts, key=cv2.contourArea)
 		((x, y), radius) = cv2.minEnclosingCircle(c)
 		M = cv2.moments(c)
 		center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
 
 		# only proceed if the radius meets a minimum size
 		if radius > 10:
 			# draw the circle and centroid on the frame,
 			# then update the list of tracked points
 			cv2.circle(frame, (int(x), int(y)), int(radius),
 				(0,255,255), 2)
 			cv2.circle(frame, center, 5, (0,0,255), -1)
                        cv2.circle(frame, center, 5, (0, 0, 255), -1)
                        ser.write("blik_x="+str(int(x-cam_res_x/2))+"\r")
                        ser.write("blik_y="+str(int(y-cam_res_y/2))+"\r")
                        ser.write("blik="+str(int(radius))+"\r")
        else:
            ser.write("blik_x="+str(0)+"\r")
            ser.write("blik_y="+str(0)+"\r")
                    
 	# update the points queue
 	pts.appendleft(center)
 
 	# loop over the set of tracked points
 	for i in xrange(1, len(pts)):
 		# if either of the tracked points are None, ignore them
 		if pts[i - 1] is None or pts[i] is None:
 			continue
 		# otherwise, compute the thickness of the line and
 		# draw the connecting lines
 		thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
 		cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
 		string= str(center).strip('[]')
 	
        cv2.putText(frame,string,(120,50), font, 1,GREEN,2,cv2.LINE_AA)
        # convert 1D array to 3D, then convert it to HSV and take the first element
        # convert array to list, draw rectangle
 
        color = cv2.cvtColor( np.uint8([[Upper]] ), cv2.COLOR_HSV2BGR)[0][0]
        # show the mask with selected pixels
        cv2.imshow("Mask", mask)
        #frame = cv2.resize(frame, (640, 480)) # resize image
        if colors:
            hsv_pixel=  cv2.cvtColor(np.uint8([colors]) , cv2.COLOR_BGR2HSV)
            cv2.putText(frame, str(hsv_pixel[-1][-1]), (120, 20),font, 1,BLUE, 2)
            cv2.putText(frame, str(int(fps)), (280, 20),font, 1,BLUE, 2)
 	cv2.imshow('Frame', frame)
        
 	key = cv2.waitKey(1) & 0xFF
       
 	end=time.time()
 	seconds =  end - start
 	if(seconds>10):
            print "Time out..."
            break
 	start = end
 	fps = 1/seconds
 	
        print "Estimated frames per seconds %8.0f"%fps
        #print(hue.get(), hue_width.get())
       
 	# if the 'q' key is pressed, stop the loop
 	if key == ord("q"):
 		break   
  # cleanup the camera and close any open windows
 camera.release()
 cv2.destroyAllWindows()

Raspberry Pi Camera V1.3