Python OpenCV movie playback triggered with serial command

A Python script that's using OpenCV to open a video, loop it, and trigger playback via a command coming from the serial port (where in my case an Arduino is connected) or a key press. Playback stops after a timer has run out and commences after another trigger event.

Trigger Quicktime video playback via Arduino

The script below is using OpenCV to open a video file, loop it, and then trigger playback when a command coming from the serial port (where in my case an Arduino is connected) is received, or when a key is pressed. Playback stops after a timer has run out and commences after another trigger event.

Aside: OpenCV with Python on a Mac

I'm in the learning phase of both Python and OpenCV (http://opencv.willowgarage.com/wiki/), and thought I'd post my successful scripts here for future reference.

After struggling for quite a while, I've finally managed to install OpenCV with bindings for Python 2.6 on my MacBook Pro. I installed opencv @2.2.0_0+python26 through MacPorts, and no problems were encountered. (When trying to add Python bindings for OpenCV to the Enthought Python Distribution I also have on my computer, all I got was frustration.) Now if I could only overcome the difficulties of installing wxpython via macports, I could just live with one single Python distribution... the 32 and 64 bit mess is going to drive me crazy one day.

The Python Script

This script opens an AVI file and displays it in a window.

  1. #!/opt/local/bin/python
  2. # Script to play a video file in response to a button press event
  3. # Pressing "x" or receiving an 'x' over the serial port starts video playback
  4. # counter_dur defines for how long movie will play
  5. # video loops until "ESC" is pressed
  6. #
  7. # uses the openCV highgui for the moment. wxpython or pyqt would be better, probably
  8. # to get rid of window decorations
  9. # Armin Hinterwirth, June 2011
  10.  
  11.  
  12. import cv
  13. import time
  14. import sys
  15. import serial
  16.  
  17. curpos = 0
  18. counter_dur = 1
  19. counter_time = time.time() # initialize timer
  20.  
  21. # Arduino object that deals with serial communication:
  22. class Arduino():
  23.  
  24. def __init__(self):
  25. self.connected = 0
  26.  
  27. def connect(self):
  28. try:
  29. serialport = "/dev/tty.usbserial-A7006xMg"
  30. # serialport needs to be set accordingly
  31. self.arduino = serial.Serial(serialport, 9600, timeout=0)
  32. self.connected = 1
  33. print 'Connected to Arduino'
  34.  
  35. except:
  36. print "Failed to connect on /dev/tty.usbserial-A7006xMg"
  37. print '(Arduino not connected)'
  38. self.connected = 0
  39.  
  40. def readSerial(self):
  41. if self.isConnected():
  42. cmd = self.arduino.read() #read one byte
  43. return cmd
  44. else:
  45. print("SerialRead unsuccessful")
  46.  
  47. def isConnected(self):
  48. if self.connected == 1:
  49. return True
  50.  
  51. def counter_expired(counter_time):
  52. cur_time = time.time()
  53. # print "%.2f" % (cur_time - counter_time) # debug: print timer values
  54.  
  55. if (cur_time - counter_time) > counter_dur:
  56. return True
  57.  
  58. def showMovieFrame(cam1,frames):
  59. frame = cv.QueryFrame(cam1)
  60. if (frame==None):
  61. sys.exit(2);
  62. curpos = int(cv.GetCaptureProperty(cam1, cv.CV_CAP_PROP_POS_FRAMES))
  63. # print "Frame: " + str(curpos)
  64. # show the image:
  65. cv.ShowImage("viewer", frame)
  66. # 'rewind' if end is reached:
  67. if (curpos > frames-2):
  68. cv.SetCaptureProperty(cam1,cv.CV_CAP_PROP_POS_FRAMES, 0)
  69.  
  70.  
  71. def main():
  72.  
  73. arduino = Arduino()
  74. arduino.connect()
  75. time.sleep(1) # give it some time
  76.  
  77. cv.NamedWindow("viewer", 0)
  78. cv.MoveWindow("viewer", 300, -20 );
  79.  
  80. cam1 = cv.CaptureFromFile('movingStripe.avi')
  81. # cam1 = cv.CaptureFromFile('grating.avi')
  82.  
  83. # get first frame and display it:
  84. frame = cv.QueryFrame(cam1)
  85. if (frame == None):
  86. sys.exit(2)
  87.  
  88. curpos = int(cv.GetCaptureProperty(cam1, cv.CV_CAP_PROP_POS_FRAMES))
  89. # print curpos
  90. cv.ShowImage("viewer", frame)
  91.  
  92. frames = long(cv.GetCaptureProperty(cam1, cv.CV_CAP_PROP_FRAME_COUNT))
  93. print "Movie length: " + str(frames) + " frames"
  94. outerloop = True # waiting for serial command
  95. innerloop = False # video playback loop
  96.  
  97. while(outerloop):
  98. if arduino.isConnected():
  99. cmd = arduino.readSerial()
  100. if (cmd == None):
  101. print("no serial received")
  102.  
  103. if (cmd == 'x'):
  104. counter_time = time.time() # set the timer
  105. innerloop = True
  106.  
  107. char = cv.WaitKey(1)
  108. # check which keys were pressed:
  109. if (char == 120):
  110. counter_time = time.time() # set the timer
  111. innerloop = True
  112. elif (char==102):
  113. showMovieFrame(cam1,frames)
  114. elif (char ==27):
  115. sys.exit(2)
  116.  
  117. while(innerloop):
  118.  
  119. showMovieFrame(cam1,frames)
  120.  
  121. char = cv.WaitKey(1)
  122. # end the program if esc button is pressed:
  123. if (char == 27):
  124. sys.exit(2)
  125.  
  126. # get out of the loop if counter is expired:
  127. if counter_expired(counter_time):
  128. print("timer expired")
  129. innerloop = False
  130.  
  131.  
  132. if __name__ == '__main__':
  133. main()

The Arduino Code

The Arduino is sending out a serial command ('x') every time the button is pressed. It also blinks for 200 ms to provide visual feedback and a sharp marker for measuring the delay between button press events and onset of playback.

  1. /*
  2.  * Button2Serial
  3.  *
  4.  * Button press leads to serial command
  5.  * Armin Hinterwirth, June 2011
  6.  *
  7.  */
  8.  
  9. const int buttonPin = 2;
  10. const int ledPin = 13;
  11. long time = 0; // timer for button debouncing
  12. long debounce = 200; // debounce time for button in ms
  13. byte buttonState = LOW;
  14.  
  15. void setup() {
  16. pinMode(buttonPin, INPUT);
  17. pinMode(ledPin, OUTPUT);
  18. Serial.begin(9600); // serial comm. for triggering
  19. }
  20.  
  21. void loop() {
  22. // read the switch state:
  23. buttonState = digitalRead(buttonPin);
  24. // turn ledPin high if button is pressed (button drops to low, then)
  25. if (buttonState == HIGH && (millis() - time) > debounce) {
  26. serialTalk();
  27. digitalWrite(ledPin, HIGH);
  28. delay(200);
  29. digitalWrite(ledPin, LOW);
  30. time = millis();
  31. }
  32. }
  33.  
  34. void serialTalk() {
  35. Serial.print("x"); // print an 'x' when button is pressed
  36. }
Category: 
Code