Search code examples
pythonmultithreadingopencvserversocket

Python OpenCV Threading


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)

Solution

  • 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)