├── README.md ├── dev ├── gui.py └── lapcounter.gif ├── lapcounter.gif └── lapcounter.py /README.md: -------------------------------------------------------------------------------- 1 | # OpenCV tests 2 | 3 | This repository contains a collection of scripts to measure things, like the lap count and lap times of a model train, based on a video or live cam feed. Created to test the performance of Batteroo sleeve, but of couse can be used for other tasks as well. 4 | 5 | ## Prerequisites 6 | 7 | On Linux, install the required OpenCV libraries like this: 8 | 9 | ``` 10 | sudo apt-get install libopencv-dev python-opencv 11 | ``` 12 | 13 | Also requires matplotlib 14 | 15 | ``` 16 | sudo apt-get install python-matplotlib python-mpltoolkits.basemap 17 | ``` 18 | -------------------------------------------------------------------------------- /dev/gui.py: -------------------------------------------------------------------------------- 1 | # gui 2 | 3 | from Tkinter import Label, Menu, PhotoImage, Tk 4 | import tkFileDialog 5 | import numpy 6 | import cv2 7 | 8 | class LapCounterGUI(Tk): 9 | def __init__(self, parent): 10 | Tk.__init__(self, parent) 11 | self.parent = parent 12 | self.initialise() 13 | 14 | def initialise(self): 15 | self.grid() 16 | 17 | self.title("Lap Counter") 18 | self.geometry("800x600") 19 | self.configure(background="#ffffff") 20 | 21 | self.menubar = Menu(self) 22 | self.filemenu = Menu(self.menubar, tearoff=0) 23 | self.filemenu.add_command(label="Open Video", command=self.open_video) 24 | self.filemenu.add_separator() 25 | self.filemenu.add_command(label="Exit", command=self.quit) 26 | self.menubar.add_cascade(label="File", menu=self.filemenu) 27 | 28 | self.config(menu=self.menubar) 29 | 30 | self.title_image_src = PhotoImage(file="lapcounter.gif") 31 | 32 | self.title_image = Label(self, image=self.title_image_src, bd=0) 33 | self.title_image.grid() 34 | 35 | def open_video(self): 36 | self.video_filename = tkFileDialog.askopenfilename() 37 | play_video(self.video_filename) 38 | 39 | 40 | def play_video(video_filename): 41 | cap = cv2.VideoCapture(video_filename) 42 | 43 | while (cap.isOpened()): 44 | ret, frame = cap.read() 45 | 46 | gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 47 | 48 | cv2.imshow('frame', gray) 49 | if cv2.waitKey(1) & 0xFF == ord("q"): 50 | break 51 | 52 | cap.release() 53 | cv2.destroyAllWindows() 54 | 55 | 56 | if __name__ == "__main__": 57 | top = LapCounterGUI(None) 58 | top.mainloop() 59 | -------------------------------------------------------------------------------- /dev/lapcounter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batterootesting/OpenCV-tests/b06d17c046132684eb38ab93ae5203f266e7567f/dev/lapcounter.gif -------------------------------------------------------------------------------- /lapcounter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batterootesting/OpenCV-tests/b06d17c046132684eb38ab93ae5203f266e7567f/lapcounter.gif -------------------------------------------------------------------------------- /lapcounter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # measures the lap count and times of a model train 4 | # usage notes: 5 | # - create a video with a fixed camera of the test run 6 | # - open a frame in an image editor and find two rectangle at opposite sides of the track, adjust "rect1" and "rect2" below 7 | # - change the name of video file below in the function call cv2.VideoCapture 8 | # - set interactive to True 9 | # - run the script. You'll see the first frame of the video 10 | # - hold down a key until the train is inside the first rect 11 | # - press "h" to display a histogram. Use a value between the two peaks for the "threshold" variable 12 | # - modify the isTrain dection function, if the simple dark/bright detection doesn't work 13 | # - open the video in a video player 14 | # - define the test cases in the startStop array 15 | # - change interactive to False 16 | # - run the script again 17 | # more details are here: http://www.eevblog.com/forum/projects/batteroo-testing/msg1098831/#msg1098831 18 | # the current values for this script were used to measure the lap counts and times for this video: http://www.frank-buss.de/batteroo/batteroo-full.mp4 19 | 20 | import cv2 21 | import sys 22 | import math 23 | from matplotlib import pyplot as plt 24 | 25 | # in interactive mode, hold down a key to show the video, press "h" to draw the histogram 26 | # and esc to stop (you have to close the histogram window, first) 27 | interactive = False 28 | 29 | # open video file 30 | video = cv2.VideoCapture("./batteroo-full.mp4") 31 | 32 | # regions for analyzing where the train is 33 | # first rect for detecting round start, second rect to arm the first rect detection 34 | rect1 = [ (387, 79), (404, 128) ] 35 | rect2 = [ (384, 457), (398, 499) ] 36 | 37 | # gray threshold to distinguish dark from bright pixels 38 | # use the interactive mode to determine a good value 39 | threshold = 80 40 | 41 | # track length, in feet 42 | trackDiameterCm = 21.8 43 | trackLengthCm = math.pi * trackDiameterCm 44 | trackLength = trackLengthCm * 0.0328084 45 | 46 | # define measure cycles, with start and stop time in seconds 47 | def hms2sec(h, m, s): 48 | return (h * 60.0 + m) * 60.0 + s 49 | startStop = [ 50 | ("fresh battery, no Batteroo sleeve", hms2sec(0, 0, 26), hms2sec(2, 8, 7)), 51 | ("fresh battery, with Batteroo sleeve", hms2sec(2, 9, 15), hms2sec(3, 7, 58)), 52 | ("battery from first test, with Batteroo sleeve", hms2sec(3, 8, 49), hms2sec(3, 12, 35)) 53 | ] 54 | 55 | # train detection: 56 | # if the number of dark pixels is greater than the number of bright pixels, the train is detected 57 | def isTrain(grayValues): 58 | dark = 0 59 | bright = 0 60 | for v in grayValues: 61 | if v > threshold: 62 | bright += 1 63 | else: 64 | dark += 1 65 | return dark > bright 66 | 67 | # get a rectangle area from an image and convert it to gray 68 | def getGrayRect(image, rect): 69 | roi = image[rect[0][1]:rect[1][1], rect[0][0]:rect[1][0]] 70 | return cv2.cvtColor(roi,cv2.COLOR_BGR2GRAY) 71 | 72 | # init runtime variables 73 | armed = False 74 | lapCount = 0 75 | lastFrameCount = 0 76 | frameCount = 0 77 | cylceRunning = False 78 | 79 | # show video information 80 | fps = video.get(cv2.cv.CV_CAP_PROP_FPS) 81 | print("fps: " + str(fps)) 82 | print("track length in cm: " + str(trackLengthCm)) 83 | print("track length in feet: " + str(trackLength)) 84 | 85 | # start lap counting 86 | print("lap number,lap start time,lap end time,total lap time,feet per minute speed") 87 | while True: 88 | if video .grab(): 89 | # get next frame 90 | flag, frame = video.retrieve() 91 | 92 | # test cycle 93 | test, start, stop = startStop[0] 94 | timeIndex = frameCount / fps 95 | frameCount += 1 96 | if not interactive: 97 | if cylceRunning: 98 | # if a cycle has started, wait for stop 99 | if timeIndex > stop: 100 | cylceRunning = False 101 | startStop = startStop[1:] 102 | if len(startStop) == 0: break 103 | else: 104 | # otherwise wait for start 105 | if timeIndex >= start: 106 | print(test + ", start at " + str(start) + " s, stop at " + str(stop) + " s") 107 | cylceRunning = True 108 | lastFrameCount = 0 109 | lapCount = 0 110 | else: 111 | continue 112 | 113 | # get the two detection areas 114 | roi1 = getGrayRect(frame, rect1) 115 | roi2 = getGrayRect(frame, rect2) 116 | 117 | # flatten values 118 | grayValues1 = roi1.ravel() 119 | grayValues2 = roi2.ravel() 120 | 121 | # check if the train is inside 122 | train1 = isTrain(grayValues1) 123 | train2 = isTrain(grayValues2) 124 | 125 | # draw marker in interactive mode and show image 126 | if interactive: 127 | if train1: 128 | cv2.rectangle(frame, rect1[0], rect1[1], (0, 255, 0), 2) 129 | if train2: 130 | cv2.rectangle(frame, rect2[0], rect2[1], (0, 255, 0), 2) 131 | cv2.imshow('video', frame) 132 | 133 | # wait until train enters rect2, then arm 134 | if train2: 135 | armed = True 136 | 137 | # when armed and train enters rect1, then count a lap and disarm 138 | if armed and not train2: 139 | if train1: 140 | armed = False 141 | # count only if train was once inside rect1 142 | if lastFrameCount > 0: 143 | lapCount += 1 144 | lapStartTime = lastFrameCount / fps 145 | lapStopTime = frameCount / fps 146 | totalLapTime = lapStopTime - lapStartTime 147 | speed = trackLength / totalLapTime * 60 148 | print(str(lapCount) + "," + str(lapStartTime) + "," + str(lapStopTime) + "," + str(totalLapTime) + "," + str(speed)) 149 | lastFrameCount = frameCount 150 | 151 | # wait for key press in interactive mode 152 | if interactive: 153 | key = cv2.waitKey() 154 | if key == 27: 155 | break 156 | if key == ord("h"): 157 | plt.hist(grayValues1, bins = 32) 158 | plt.hist(grayValues2, bins = 32) 159 | plt.show() 160 | print("frames: " + str(frameCount)) 161 | --------------------------------------------------------------------------------