A Python script reads png images in from a socket and tries to display then with OpenCV like a slow video. There are 2 classes server.py and Worker.py. Server.py receives a raw png from a socket and places it on queue. In its own thread, a worker dequeues the png and displays it. Only the first image displays properly. Others images are not visibile (i.e. the first image remains), though ShowImage and WaitKey are called.
Is the thread model right? It seems like the server socket thread prevents the OpenCV thread from displaying. Is there a way to put Server Sockets on a background thread? Is calling ShowWIndow and WaitKey from the background thread correct? I don't know much about Python or OpenCV threading on a mac. Any comments and suggestions will be appreciated.
server.py:
#! /usr/bin/env python
import sys
import SocketServer
import socket
import subprocess
import time
import cv
import Worker
import ShowImage
HOST = 'andrew-rosenblums-macbook-pro.local'
PORT = 3001
FRAME_SIZE = 144*192
data = ''
worker = Worker.Worker()
class SingleTCPHandler(SocketServer.BaseRequestHandler):
imagesSaved = 0
"One instance per connection. Override handle(self) to customize action."
def handle(self):
# self.request is the client connection
print "connection received.."
while True:
#print "calling rcv"
messageLength = self.request.recv(6) # Read 6 ascii char image size
cv.WaitKey(30)
if (len(messageLength) > 0):
print "messageLength=" + messageLength
iLength = int(messageLength)
message = ''
while (iLength > 0):
if (iLength > 1024):
chunk = self.request.recv(1024)
else:
chunk = self.request.recv(iLength)
iLength -= len(chunk)
message += chunk
print "rcvd imsg of len=" + str(len(message))
worker.write(message)
if (SingleTCPHandler.imagesSaved == 0):
SingleTCPHandler.imagesSaved += 1
print "closing stream"
self.request.close()
print "done receiving"
def finish(self):
print "finish called"
class SimpleServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
# Ctrl-C will cleanly kill all spawned threads
daemon_threads = True
# much faster rebinding
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass):
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
if __name__ == "__main__":
server = SimpleServer((HOST, PORT), SingleTCPHandler)
# terminate with Ctrl-C
try:
print "waiting for connections..."
server.serve_forever()
except KeyboardInterrupt:
sys.exit(0)
Worker.py
#! /usr/bin/env python
import time
from threading import Thread
from Queue import Queue
import sys
import cv
import cv2
import numpy as np
import Image
from cStringIO import StringIO
class Worker(Thread):
count = 0
def __init__(self):
Thread.__init__(self)
self.cvImage = None
cv.NamedWindow('display')
cv.MoveWindow('display', 10, 10)
self.queue = Queue()
self.writer = None
# Daemon threads won't prevent process from exiting
self.setDaemon(True)
# Start ourselves automatically
self.start()
print "Worker started"
def run(self):
writer = None
while 1:
frame = None
try:
#frame = self.queue.get(block=False)
frame = self.queue.get()
print "display izeof rawImage=" + str(len(frame))
#convert to mat
pilImage = Image.open(StringIO(frame));#.convert("RGB");
bgrImage = np.array(pilImage)
cvBgrImage = cv.fromarray(bgrImage)
self.cvImage = cv.CreateImage(cv.GetSize(cvBgrImage),8,3)
cv.CvtColor(cvBgrImage, self.cvImage, cv.CV_BGR2RGB)
#show it
cv.ShowImage('display', self.cvImage)
cv.WaitKey(30)
self.cvImage = None
except:
frame = None
print "done with thread"
# Requests from main thread
def write(self, frame):
self.queue.put(frame)
def stop(self):
self.queue.put(None)
The answer is simple: OpenCV's ShowImage
and WaitKey
have to be in the main thread. I guess this is the GUI thread. ServerSockets was pushed to its own thread, which is surprisingly easy in Python.
All you need is something like:
UDPThread = Thread(target = UDPServer.run)
s.start(UPDThread)