├── README.md ├── .gitignore ├── time_lapse.py └── FindGolf.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | # findball-opencv 3 | 4 | ## all 5 | * 功能描述 6 | 7 | 实现对一个黄色小球的动态捕捉,输出实时信息,后期实现控制机械臂来抓取小球 8 | 9 | * 开发环境 10 | 11 | python2.7.10 + opencv3.0.0+_树莓派 12 | 13 | ### update 14 | 大学实验课作业 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Python 2 | temptest.py 3 | httptest.py 4 | golftest.py 5 | bookexample.py 6 | 7 | #Picture 8 | 2.png 9 | 3.jpg 10 | 5.jpg 11 | beautiful girl.jpg 12 | golf1.jpg 13 | mutilgolf.jpg 14 | mutilgolf1.jpg 15 | save.jpg 16 | 17 | 18 | #other 19 | golfdetct 20 | opencvtoturialtest 21 | .idea/ 22 | findtheball.avi 23 | -------------------------------------------------------------------------------- /time_lapse.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | import cv2 3 | import time 4 | 5 | interval = 10 # seconds,delay-time 6 | num_frames = 3 # number of frame 7 | out_fps = 24 # 摄像头输出帧数 8 | 9 | capture = cv2.VideoCapture(0) 10 | size = (320, 240) 11 | video = cv2.VideoWriter("time_lapse.avi", cv2.VideoWriter_fourcc(*'XVID'), out_fps, size) 12 | 13 | # for low quality webcams, discard the starting unstable frames 14 | for i in xrange(42): 15 | capture.read() 16 | 17 | # capture frames to video 18 | for i in xrange(num_frames): 19 | _, frame = capture.read() 20 | video.write(frame) 21 | # Optional, in case you need the frames for GIF or so 22 | filename = '{:4}.png'.format(i).replace(' ', '0') 23 | cv2.imwrite(filename, frame) 24 | 25 | print('Frame {} is captured.'.format(i)) 26 | time.sleep(interval) 27 | 28 | capture.release() 29 | video.release() 30 | -------------------------------------------------------------------------------- /FindGolf.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python 2 | # -*-encoding:utf-8 -*- 3 | """ 4 | __author__ = 'YingqunZhong' 5 | version:1.1 6 | """ 7 | import numpy as np 8 | import cv2 9 | import math 10 | import sys 11 | import time 12 | import datetime 13 | 14 | 15 | # SCREEN_WIDTH = 320 16 | # SCREEN_HEIGHT = 240 17 | # 18 | # GREEN_PIN = 5 19 | # RED_PIN = 4 20 | 21 | # 设置黄色乒乓球颜色范围 22 | # HSV_MIN = np.array((14, 90, 90)) 23 | # HSV_MAX = np.array((45, 255, 255)) 24 | 25 | def noting(x): 26 | pass 27 | 28 | 29 | def distance(x1, y1, x2, y2): 30 | m = abs(x2 - x1) 31 | n = abs(y2 - y1) 32 | r = math.sqrt(m * m + n * n) 33 | return r 34 | 35 | 36 | def find_ball(capture, noimage, nothreshold): 37 | global HSV_MIN 38 | global HSV_MAX 39 | cv2.createTrackbar('Hl', 'thresholded image', 15, 255, noting) 40 | cv2.createTrackbar('Sl', 'thresholded image', 90, 255, noting) 41 | cv2.createTrackbar('Vl', 'thresholded image', 90, 255, noting) 42 | cv2.createTrackbar('Hh', 'thresholded image', 45, 255, noting) 43 | cv2.createTrackbar('Sh', 'thresholded image', 200, 255, noting) 44 | cv2.createTrackbar('Vh', 'thresholded image', 200, 255, noting) 45 | Hlow = cv2.getTrackbarPos('Hl', 'thresholded image') 46 | Slow = cv2.getTrackbarPos('Sl', 'thresholded image') 47 | Vlow = cv2.getTrackbarPos('Vl', 'thresholded image') 48 | Hhigh = cv2.getTrackbarPos('Hh', 'thresholded image') 49 | Shigh = cv2.getTrackbarPos('Sh', 'thresholded image') 50 | Vhigh = cv2.getTrackbarPos('Vh', 'thresholded image') 51 | 52 | HSV_MIN = np.array((int(Hlow), int(Slow), int(Vlow))) 53 | HSV_MAX = np.array((int(Hhigh), int(Shigh), int(Vhigh))) 54 | 55 | Cx, Cy = 0, 0 56 | maxdiag = 0 # 判断面积 57 | fps = 30 # 58 | 59 | videoout = cv2.VideoWriter('findtheball.avi', cv2.VideoWriter_fourcc(*'XVID'), fps, (640, 480)) 60 | ret, frame = capture.read() # 获取每一帧 61 | text = "searching golf..." 62 | 63 | frame = cv2.resize(frame, (0, 0), fx=0.8, fy=0.8) # set window's size 64 | 65 | if frame is not None: 66 | hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # convert BGR to HSV 67 | # 色度,饱和度,明度(纯度) 68 | # hsv_frame = cv2.GaussianBlur(hsv_frame, (6,6), 0) # 高斯滤波 69 | h, s, v = cv2.split(hsv_frame) 70 | thresholded = cv2.inRange(hsv_frame, HSV_MIN, HSV_MAX) # inrange函数二值化 71 | 72 | # 开运算,消除白噪声 73 | thresholded = cv2.erode(thresholded, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))) 74 | thresholded = cv2.dilate(thresholded, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))) 75 | # 闭运算,消除前景内部空洞或者小黑点 76 | thresholded = cv2.dilate(thresholded, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))) 77 | thresholded = cv2.erode(thresholded, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))) 78 | 79 | image, contours, hierarchy = cv2.findContours(thresholded.copy(), 80 | cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 81 | 82 | pass # version2.0加入圆检测 83 | 84 | for cnt in contours: 85 | 86 | if cv2.contourArea(cnt) < 100: # 76 87 | continue 88 | 89 | x, y, w, h = cv2.boundingRect(cnt) # (x,y)轮廓左上角坐标,(w,h)轮廓的宽和高 90 | cx, cy = x + w / 2, y + h / 2 91 | current_diag = math.sqrt(w * w + h * h) / 2 92 | current_diag = current_diag / (math.sqrt(2)) 93 | if (current_diag > maxdiag): 94 | maxdiag, Cx, Cy = current_diag, int(cx), int(cy) # change 95 | # maxdiag, Cx, Cy = 1.20 * current_diag, int(1.023 * cx), int(1.06 * cy) # change 96 | 97 | circles = cv2.HoughCircles(v, cv2.HOUGH_GRADIENT, 1, 20, param1=90, 98 | param2=40, minRadius=0, maxRadius=100) 99 | 100 | if circles is not None: 101 | circles = np.round(circles[0, :]).astype("int") 102 | 103 | for (x, y, r) in circles: 104 | cv2.circle(v, (x, y), r, (0, 0, 238), 4) 105 | if distance(x, y, Cx, Cy) < r: 106 | cv2.circle(frame, (x, y), r, (0, 0, 238), 4) 107 | cv2.circle(frame, (x, y), 2, (0, 0, 255), 5) 108 | cv2.circle(frame, (Cx, Cy), int(maxdiag), (0, 255, 0), 3) 109 | cv2.circle(frame, (Cx, Cy), 2, (255, 0, 0), 5) 110 | 111 | # for (x, y, r) in circles: 112 | # cv2.circle(frame, (x, y), r, (0, 255, 255), 4) 113 | # cv2.circle(frame, (x, y), 2, (0, 0, 255), 5) 114 | # if maxdiag > 0: 115 | # cv2.circle(frame, (Cx, Cy), int(maxdiag), (0, 255, 0), 3) 116 | # cv2.circle(frame, (Cx, Cy), 2, (255, 0, 0), 5) 117 | # # 118 | # (x, y), radius = cv2.minEnclosingTriangle(cnt) 119 | # current_diag=int (radius) 120 | # if(current_diag>maxdiag): 121 | # maxdiag,Cx,Cy=1.384*current_diag,int(1.023*x),int(1.06*y) 122 | # 123 | # if maxdiag>0: 124 | # cv2.circle(frame,(Cx,Cy),int(maxdiag),(0,255,0),3) 125 | 126 | # 用户交互界面 127 | text = "completed! Find the ball!" 128 | cv2.putText(frame, "Work-Status: {}".format(text), (10, 20), 129 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) 130 | cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"), 131 | (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1) 132 | 133 | if noimage == False: 134 | cv2.imshow("H_channel", h) 135 | cv2.imshow("S_channel", s) 136 | cv2.imshow("V_channel", v) 137 | cv2.imshow("original image", frame) 138 | 139 | if nothreshold == False: 140 | cv2.imshow("thresholded image", thresholded) 141 | 142 | if ret is True: 143 | frame = cv2.flip(frame, 0) 144 | videoout.write(frame) 145 | 146 | key = cv2.waitKey(1) & 0xff 147 | if key == 27: 148 | videoout.release() 149 | capture.release() 150 | cv2.destroyAllWindows() 151 | 152 | else: 153 | print "Cannot get frame" 154 | 155 | return (maxdiag, Cx, Cy) # 圆的半径和圆点坐标 156 | 157 | 158 | def main(): 159 | noimage = False 160 | nothreshold = False 161 | 162 | print('Initialize Camera...') # 163 | capture = cv2.VideoCapture(0) 164 | 165 | if capture is not None: 166 | print('OK...') 167 | else: 168 | return 169 | 170 | # 显示小球在摄像头区域坐标信息和帧数 171 | frames = 0 172 | start_time = time.time() 173 | while True: 174 | Radius, center_x, center_y = find_ball(capture, noimage, nothreshold) 175 | 176 | frames += 1 177 | currtime = time.time() 178 | numsecs = currtime - start_time 179 | fps = frames / numsecs # 每秒帧数 180 | 181 | sys.stdout.write("\t\t\r") 182 | sys.stdout.write("Found the ball at:(x:%d,y:%d) Radius=%d" % (center_x, center_y, Radius) + 183 | " fps=%d" % fps) 184 | sys.stdout.flush() 185 | 186 | return 187 | 188 | 189 | if __name__ == '__main__': 190 | sys.exit(main()) 191 | --------------------------------------------------------------------------------