In addition to what Chris said, is there a reason why you're reinventing
the wheel instead of using available components?
Hi Carsten,
The eventual goal here is towards a streaming two-way server, which wouldn't use the http, but something more like RTP/RTCP/H.323/ which I'm learning. It's also a general socket learning exercise for me, as I haven't used them very much.
I was also using PyDShowCam before, and have switched to VideoCapture since I'll need to recompile PyDShowCam for py2.4.
I will look into the wx Chris suggested this weekend; more stuff I haven't used...
Thanks,
Ray
For anyone interested, I attached a quick wx I made last night that monitors a USB webcam, will create "dark" frames (a la astronomy), and serve up images to web browsers (thus the question). The browser/monitor would actually work nicely with a web page that has a _javascript_ function to reload the image x times/second. I'll try replacing the PIL with the wx function(s) and benchmark if they work.
#Boa:Frame:Frame1 import gc from time import clock from numpy import fromstring, add, subtract, divide, where, zeros
import socket import wx #import PyDShowCam import VideoCapture from StringIO import StringIO from PIL import Image def create(parent): return Frame1(parent) [wxID_FRAME1, wxID_FRAME1BUTTONCONNECT, wxID_FRAME1BUTTONPROPERTIES, wxID_FRAME1BUTTONSERVER, wxID_FRAME1BUTTONSETDARK, wxID_FRAME1BUTTONSTOP, wxID_FRAME1CHECKBOXDIFF, wxID_FRAME1PANEL1, wxID_FRAME1STATICBITMAPVIDEO, wxID_FRAME1STATUSBAR1, wxID_FRAME1TEXTCTRLADDRESS, wxID_FRAME1TOOLBAR1, wxID_FRAME1WINDOWVIDEO, ] = [wx.NewId() for _init_ctrls in range(13)] class Frame1(wx.Frame): def _init_coll_toolBar1_Tools(self, parent): # generated method, don't edit parent.AddControl(control=self.textCtrlAddress) parent.AddControl(control=self.buttonConnect) parent.AddControl(control=self.buttonStop) parent.AddControl(control=self.buttonProperties) parent.AddControl(control=self.checkBoxDiff) parent.AddControl(control=self.buttonSetDark) parent.AddControl(control=self.buttonServer) parent.Realize() def _init_coll_statusBar1_Fields(self, parent): # generated method, don't edit parent.SetFieldsCount(3) parent.SetStatusText(number=0, text='"frame:0,\t0fps"') parent.SetStatusText(number=1, text='') parent.SetStatusText(number=2, text='') parent.SetStatusWidths([200, -1, 100]) def _init_ctrls(self, prnt): # generated method, don't edit wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt, pos=wx.Point(278, 392), size=wx.Size(746, 377), style=wx.DEFAULT_FRAME_STYLE, title='Confer') self.SetClientSize(wx.Size(738, 350)) self.statusBar1 = wx.StatusBar(id=wxID_FRAME1STATUSBAR1, name='statusBar1', parent=self, style=0) self._init_coll_statusBar1_Fields(self.statusBar1) self.SetStatusBar(self.statusBar1) self.toolBar1 = wx.ToolBar(id=wxID_FRAME1TOOLBAR1, name='toolBar1', parent=self, pos=wx.Point(0, 0), size=wx.Size(767, 27), style=wx.TB_HORIZONTAL) self.SetToolBar(self.toolBar1) self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self, pos=wx.Point(0, 27), size=wx.Size(738, 303), style=wx.TAB_TRAVERSAL) self.panel1.SetBackgroundColour(wx.Colour(255, 255, 255)) self.windowVideo = wx.Window(id=wxID_FRAME1WINDOWVIDEO, name='windowVideo', parent=self.panel1, pos=wx.Point(8, 8), size=wx.Size(352, 288), style=0) self.windowVideo.SetBackgroundColour(wx.Colour(0, 0, 0)) self.textCtrlAddress = wx.TextCtrl(id=wxID_FRAME1TEXTCTRLADDRESS, name='textCtrlAddress', parent=self.toolBar1, pos=wx.Point(0, 0), size=wx.Size(100, 21), style=0, value='192.168.0.1') self.textCtrlAddress.SetToolTipString('Enter the remote address here') self.buttonConnect = wx.Button(id=wxID_FRAME1BUTTONCONNECT, label='Connect', name='buttonConnect', parent=self.toolBar1, pos=wx.Point(100, 1), size=wx.Size(60, 19), style=0) self.buttonConnect.Bind(wx.EVT_BUTTON, self.OnButtonConnectButton, id=wxID_FRAME1BUTTONCONNECT) self.buttonStop = wx.Button(id=wxID_FRAME1BUTTONSTOP, label='Stop', name='buttonStop', parent=self.toolBar1, pos=wx.Point(160, 1), size=wx.Size(49, 19), style=0) self.buttonStop.Bind(wx.EVT_BUTTON, self.OnButtonStopButton, id=wxID_FRAME1BUTTONSTOP) self.buttonProperties = wx.Button(id=wxID_FRAME1BUTTONPROPERTIES, label='Cam Properties', name='buttonProperties', parent=self.toolBar1, pos=wx.Point(209, 1), size=wx.Size(88, 19), style=0) self.buttonProperties.Bind(wx.EVT_BUTTON, self.OnButtonPropertiesButton, id=wxID_FRAME1BUTTONPROPERTIES) self.staticBitmapVideo = wx.StaticBitmap(bitmap=wx.NullBitmap, id=wxID_FRAME1STATICBITMAPVIDEO, name='staticBitmapVideo', parent=self.panel1, pos=wx.Point(374, 8), size=wx.Size(352, 288), style=0) self.checkBoxDiff = wx.CheckBox(id=wxID_FRAME1CHECKBOXDIFF, label='diff frames', name='checkBoxDiff', parent=self.toolBar1, pos=wx.Point(297, 4), size=wx.Size(70, 13), style=0) self.checkBoxDiff.SetValue(False) self.buttonSetDark = wx.Button(id=wxID_FRAME1BUTTONSETDARK, label='Dark frame', name='buttonSetDark', parent=self.toolBar1, pos=wx.Point(367, 1), size=wx.Size(75, 19), style=0) self.buttonSetDark.Bind(wx.EVT_BUTTON, self.OnButtonSetDarkButton, id=wxID_FRAME1BUTTONSETDARK) self.buttonServer = wx.Button(id=wxID_FRAME1BUTTONSERVER, label='Server', name='buttonServer', parent=self.toolBar1, pos=wx.Point(456, 0), size=wx.Size(75, 23), style=0) self.buttonServer.Bind(wx.EVT_BUTTON, self.OnButtonServerButton, id=wxID_FRAME1BUTTONSERVER) self._init_coll_toolBar1_Tools(self.toolBar1) def __init__(self, parent): self._init_ctrls(parent) self.cam = None self.buttonStop.Enable(True) self.darkFrame = None wx.InitAllImageHandlers() self.Show() def OnButtonConnectButton(self, event): self.cam = VideoCapture.Device(devnum=0, showVideoWindow=0) buff, width, height = self.cam.dev.getbuffer() buffArrayLast = fromstring(buff, 'uint8')[-1::-1].copy() im = wx.EmptyImage(width, height) self.buttonConnect.Enable(False) self.buttonStop.Enable(True) getbuffer = self.cam.dev.getbuffer darkFrame = self.darkFrame if darkFrame: darkArray = self.darkArray im_SetData = im.SetData im_Mirror = im.Mirror BitmapFromImage = wx.BitmapFromImage SetBitmap = self.staticBitmapVideo.SetBitmap SetStatusText = self.statusBar1.SetStatusText isDiff = self.checkBoxDiff.GetValue Yield = wx.Yield frame = 0 t1 = clock() while self.cam: buff, width, height = getbuffer() buffArray = fromstring(buff, 'uint8')[-1::-1].copy() if darkFrame: buffArray = where(buffArray>darkArray, subtract(buffArray, darkArray), 0) if isDiff(): im_SetData(subtract(buffArray, buffArrayLast).tostring()) buffArrayLast = buffArray.copy() else: im_SetData(buffArray.tostring()) SetBitmap(BitmapFromImage(im_Mirror())) Yield() frame += 1 if frame % 10 == 0: t2 = clock()-t1 SetStatusText(number=0, text="frame:%d,\t%.3ffps" % (frame, round(10/(t2), 3))) t1 = clock() def OnButtonStopButton(self, event): self.cam = None gc.collect self.buttonConnect.Enable(True) self.buttonServer.Enable(True) self.buttonStop.Enable(False) def OnButtonPropertiesButton(self, event): if(self.cam != None): ## explicitely remove references to the Capture device ## and run garbage collection self.cam = None gc.collect() self.SetStatusText('Device set to None') try: cam = Device(devnum=0, showVideoWindow=1) ## display the dialog-boxes which allow changing the video size cam.dev.displayCaptureFilterProperties() cam = None except: dlg = wx.MessageDialog(self, 'Cam device does not support displayCaptureFilterProperties()', 'Error', wx.OK | wx.ICON_INFORMATION) try: dlg.ShowModal() finally: dlg.Destroy() print '\'Device.displayCaptureFilterProperties\' call not done.' def OnButtonSetDarkButton(self, event): self.cam = VideoCapture.Device(devnum=0, showVideoWindow=0) buff, width, height = self.cam.dev.getbuffer() darkArray = zeros((width*height*3), 'uint8') im = wx.EmptyImage(width, height) getbuffer = self.cam.dev.getbuffer im_SetData = im.SetData im_Mirror = im.Mirror BitmapFromImage = wx.BitmapFromImage SetBitmap = self.staticBitmapVideo.SetBitmap SetStatusText = self.statusBar1.SetStatusText frame = 0 while frame<100: buff, width, height = getbuffer() #print fromstring(buff, 'uint8')[-1::-1].copy().shape, darkArray.shape darkArray = add(fromstring(buff, 'uint8')[-1::-1].copy(), darkArray) frame += 1 SetStatusText(number=0, text="frame:%d" % (frame)) im_SetData(darkArray.tostring()) self.bmp = BitmapFromImage(im_Mirror()) SetBitmap(self.bmp) #imPIL = (Image.frombuffer("RGB", (width, height), darkArray.tostring(), "raw", "RGB", 0, 1) ).transpose(Image.FLIP_LEFT_RIGHT) self.cam = None gc.collect #print darkArray[:21] #print divide(darkArray, 100).astype('uint8')[21000:21200] self.darkArray = divide(darkArray, 10).astype('uint8') #self.bmpFile = StringIO() #print dir(self.bmpFile) #im.SaveFile(self.bmpFile, wx.BITMAP_TYPE_JPEG) #self.bmp.SaveFile('temp.jpg', wx.BITMAP_TYPE_JPEG) #self.PILFile = StringIO() #imPIL.save(self.PILFile, "JPEG") def OnButtonServerButton(self, event): self.buttonStop.Enable(True) self.buttonServer.Enable(False) HOST = '' # Symbolic name meaning the local host PORT = 8888 # Arbitrary non-privileged port try: del(self.cam) except: pass self.cam = VideoCapture.Device(devnum=0, showVideoWindow=0) buff, width, height = self.cam.dev.getbuffer() buffArrayLast = fromstring(buff, 'uint8')[-1::-1].copy() im = wx.EmptyImage(width, height) self.buttonConnect.Enable(False) self.buttonStop.Enable(True) getbuffer = self.cam.dev.getbuffer darkFrame = self.darkFrame if darkFrame: darkArray = self.darkArray im_SetData = im.SetData im_Mirror = im.Mirror BitmapFromImage = wx.BitmapFromImage SetBitmap = self.staticBitmapVideo.SetBitmap SetStatusText = self.statusBar1.SetStatusText isDiff = self.checkBoxDiff.GetValue Yield = wx.Yield count = 0 while self.buttonStop.IsEnabled(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(1) conn, addr = s.accept() print 'Connected by', addr while self.buttonStop.IsEnabled(): data = conn.recv(1024) if not data: break print "Data received:" if data[:3] == 'GET': buff, width, height = getbuffer() imPIL = (Image.frombuffer("RGB", (width, height), buff, "raw", "RGB", 0, 1) ).transpose(Image.FLIP_LEFT_RIGHT) PILFile = StringIO() imPIL.save(PILFile, "JPEG") imString = PILFile.getvalue() try: conn.send("HTTP/1.0 200 OK"+"\015\012") conn.send("Server: RJS_video/0.0.1"+"\015\012") #conn.send("Content-type: text/html; charset=ISO-8859-1"+"\015\012") conn.send("Content-type: image/jpeg"+"\015\012") conn.send("Content-Length: "+str(len(imString))+"\015\012") conn.send("\015\012") conn.send(imString) except: print 'socket error' break count = count + 1 print 'len:', len(data), '\n'#, data #break else: conn.send('err') print 'err' print count, #if count == 5: #break print 'Closing connection to', addr s.close() Yield() PILFile.close() del(self.cam) def getimage(stream, intoImage=None): pass """ 0135 ##Returns a wxImage of the stream specified 0136 0137 # try to read the entire thing in one gulp 0138 data=stream.read() 0139 # save hex version for debugging 0140 # f=open(file+".hex", "w") 0141 # f.write(common.datatohexstring(data)) 0142 # f.close() 0143 0144 palettes={} 0145 0146 ### verify format 0147 0148 # header 0149 assert data[0x00:0x04]=='BCI\x00' 0150 # file length 0151 assert readlsb(data[0x04:0x08])<=len(data) # this would be == but the bci tool doesn't truncate the file! 0152 # image offset 0153 imageoffset=readlsb(data[0x08:0x0b]) 0154 assert imageoffset<len(data) 0155 # ? (1) 0156 assert readlsb(data[0x0c:0x0e])==1 0157 # width, height 0158 width=readlsb(data[0x0e:0x10]) 0159 height=readlsb(data[0x10:0x12]) 0160 assert width>0 and height>0 0161 # number of objects/frames/palettes? no idea on order 0162 numitem1=readlsb(data[0x12:0x14]) 0163 numitem2=readlsb(data[0x14:0x16]) 0164 numitem3=readlsb(data[0x16:0x18]) 0165 # print "number of objects/frames/palettes? no idea on order: %d, %d, %d" % (numitem1, numitem2, numitem3) 0166 numpalettes=numitem1 # just a guess 0167 numotherthing=numitem2 # no idea what they are, possibly 'frames' as in the doc 0168 numimages=numitem3 # images, probably 'object' as in the doc 0169 # ? (0) 0170 assert readlsb(data[0x18:0x1a])==0 0171 # palette depth? 0172 bpp=readlsb(data[0x1a:0x1c]) 0173 # read the palettes 0174 offset=0x1c 0175 for _ in range(numpalettes): 0176 id=readlsb(data[offset:offset+2]) 0177 # print "palette id",id 0178 offset+=2 0179 numentries=readlsb(data[offset:offset+2]) 0180 # print "contains",numentries,"entries" 0181 offset+=2 0182 # f=open(file+".palette."+`id`+".hex", "w") 0183 # f.write(common.datatohexstring(data[offset:offset+numentries*4])) 0184 # f.close() 0185 pal=BCIPalette(data[offset:offset+numentries*4]) 0186 offset+=numentries*4 0187 palettes[id]=pal 0188 0189 0190 # some other type of object, possibly frames as in the doc 0191 for _ in range(numotherthing): 0192 # we just ignore the contents for the moment 0193 # print common.datatohexstring(data[offset:offset+0x14]) 0194 offset+=0x14 0195 0196 # images 0197 for _ in range(numimages): 0198 szdata=readlsb(data[offset:offset+4]) 0199 width=readlsb(data[offset+4:offset+6]) 0200 height=readlsb(data[offset+6:offset+8]) 0201 id1=readlsb(data[offset+8:offset+0xa]) # image id? 0202 id2=readlsb(data[offset+0xa:offset+0xc]) # palette id? 0203 offset+=0xc 0204 buf=data[offset:offset+szdata] 0205 res=zlib.decompress(buf) 0206 # f=open(file+".image."+`id1`+".hex", "w") 0207 # f.write(common.datatohexstring(res)) 0208 # f.close() 0209 0210 img=MyImage(width, height, res, palettes[id2]) 0211 0212 return img.toImage(intoImage) """
#!/usr/bin/env python #Boa:App:BoaApp import wx import Frame1 modules ={'Frame1': [1, 'Main frame of Application', 'Frame1.py']} class BoaApp(wx.App): def OnInit(self): wx.InitAllImageHandlers() self.main = Frame1.create(None) self.main.Show() self.SetTopWindow(self.main) return True def main(): #import psyco #psyco.full() application = BoaApp(0) application.MainLoop() if __name__ == '__main__': main()
-- http://mail.python.org/mailman/listinfo/python-list