CodeSnippits Dynamic Webcam
From PyWiki
Contents |
Dynamic Webcam
Here is one solution to have a webcam feeded material.
Here is how it works:
- Image acquisition is made in one thread with the openCV library
- The material update is performed in the main thread with code taken from this wiki at Dynamic Textures
It was tested under:
- WinXP SP3
- python-ogre 1.6
- openGL renderer (directX not working)
Please update here if you have it working with another platform.
Prerequisites
openCV dlls
Get the openCV library. Take the one called opencv-win. When issuing this article the current version is opencv-win-1.1pre1.
Once installed, you should have the dlls under C:\Program Files\OpenCV\bin. Here, you have two options:
Either:
- Update your PATH environment variable so that the dlls are taken into account (openCV installer can do it for you).
Or:
- Copy the following dlls to your working directory:
- cv110.dll
- cxcore110.dll
- highgui110.dll
openCV python interface
You will need to get Gary Bishop's python interface to openCV. The full story is here, but here is the short todo list for impatients:
- Get cvtypes.zip from here
- Take CVtypes.py from the zip file and place it in your working dir.
- Update CVTypes.py lines 42 to 44:
From:
_cxDLL = cdll.cxcore100 _cvDLL = cdll.cv100 _hgDLL = cdll.highgui100
To:
_cxDLL = cdll.cxcore110 _cvDLL = cdll.cv110 _hgDLL = cdll.highgui110
- Update CVTypes.py line 4167:
From
cvConvertPointsHomogenious = cfunc('cvConvertPointsHomogenious', _cvDLL, None,
To:
cvConvertPointsHomogenious = cfunc('cvConvertPointsHomogeneous', _cvDLL, None,
Check webcam acquisition
Edit a test python file with the following code:
from CVtypes import cv win = 'Show Cam' cv.NamedWindow(win) cap = cv.CreateCameraCapture(0) while cv.WaitKey(1) != 27: img = cv.QueryFrame(cap) cv.ShowImage(win, img)
Plug your USB webcam, and run this test app... you should see your face. If not, well, report on the forum, something went wrong...
Webcam Demo app
Here is the full Demo_webcam.py. Place it under demos\webcam so that there is no mess when looking for SampleFramework.
import sys sys.path.insert(0,'..') import os import ogre.renderer.OGRE as ogre import ctypes import SampleFramework as sf # For WEBCAM from CVtypes import cv import threading import traceback import time # WEBCAM RESOLUTION WEBCAM_WIDTH = 160 WEBCAM_HEIGHT = 120 # CROPPED AREA FROM WEBCAM TOPLEFT CORNER (SAME AS WEBCAM_WIDTH AND WEBCAM_HEIGHT FOR NO CROP) WIDTH = 160 HEIGHT = 120 # THREADS COMMUNICATION pixBuf = [] # Shared data between webcam thread and main thread newPix = False # Inform main thread that new data is available waitPix = True # Inform webcam thread that new data is needed lock = threading.Lock() # Resources lock class MyFeed ( threading.Thread ): " Take care of images acquisition " def __init__(self,nada): print "Starting webcam" try: self.cap = cv.CreateCameraCapture(0) # Set width/height cv.SetCaptureProperty( self.cap, cv.CAP_PROP_FRAME_WIDTH, WEBCAM_WIDTH) cv.SetCaptureProperty( self.cap, cv.CAP_PROP_FRAME_HEIGHT, WEBCAM_HEIGHT ) # Acquire one image, will throw one exeption in case of no webcam present tmp = cv.QueryFrame(self.cap) tmp2 = cv.ImageAsBuffer(tmp) except: print "No webcam here" self.cap = None self.running = True self.readyToStop = False threading.Thread.__init__ ( self ) def stop(self): if self.running: # Normal stop self.running = False if self.cap == None: print "No webcam to stop" else: print "Waiting for webcam to stop...", while not self.readyToStop: pass print "ok" cv.ReleaseCapture(self.cap) else: # In case stop in called when already stopped return None def run ( self ): # Check if we may run if self.cap == None: print "No webcam present, aborting thread" return True # We may run! global pixBuf,newPix,waitPix,lock while self.running: if waitPix: # Acquire image img = cv.QueryFrame(self.cap) # Do not process if abort in progress if not self.running: break print "New image" lock.acquire() pixBuf = cv.ImageAsBuffer(img) newPix = True waitPix = False lock.release() else: time.sleep(0.05) # Once loop is finished, thread may be terminated self.readyToStop = True class textureListener(ogre.FrameListener): def __init__(self, app): ogre.FrameListener.__init__(self) self.app = app self.grnVal = 0 self.fading = True def frameStarted(self, frameEvent): # Update material from webcam image global pixBuf,newPix,waitPix,lock if newPix: print "Updating" try: lock.acquire() ret = self.app.setTextureManual(self.app.mTex, WIDTH,HEIGHT, pixBuf) if not ret: # Aborting lock.release() return False # Tell next frameStarted that this image was already taken into account newPix = False # Tell thread to acquire a new image waitPix = True lock.release() except: print "Exception:" traceback.print_exc(file=sys.stdout) return True class textureTest(sf.Application): def __init__(self): sf.Application.__init__(self) def _createScene(self): self.mTex = ogre.TextureManager.getSingleton().createManual( "DynamicTexture", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, ogre.TEX_TYPE_2D, WIDTH, HEIGHT, 0, ogre.PF_BYTE_BGRA, ogre.TU_DEFAULT) colArray = [0,255,0,128] * WIDTH * HEIGHT self.setTextureManual(self.mTex, WIDTH, HEIGHT, colArray) self.material = ogre.MaterialManager.getSingleton().create( "DynamicTextureMaterial", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME) self.material.getTechnique(0).getPass(0).createTextureUnitState("DynamicTexture") self.material.getTechnique(0).getPass(0).setSceneBlending(ogre.SBT_TRANSPARENT_ALPHA) self.ent = self.sceneManager.createEntity("mrben", "cube.mesh") self.ent.setMaterialName("DynamicTextureMaterial") self.node = self.sceneManager.getRootSceneNode().createChildSceneNode('Knot3Node', (-100, -10, -200)) self.node.attachObject(self.ent) self.node.setPosition(ogre.Vector3(125, 10, 0)) self.node.setScale(2,2,2) self.node.yaw(0.4) self.node.roll(0.2) self.lstnr = textureListener(self) ogre.Root.getSingleton().addFrameListener(self.lstnr) #Texture is a manually created Texture #dataArray is an array of pixel colour data def setTextureManual(self, texture, width, height, dataArray): # Get the pixel buffer pixelBuffer = texture.getBuffer() # Lock the pixel buffer and get a pixel box pointer = pixelBuffer.lock(0,width*height*4,ogre.HardwareBuffer.HBL_NORMAL) #number 4 here is use to accommodate the bytes for r, g, b, a may vary with different pixel formats storageclass = ctypes.c_uint8 * (width*height*4) cbuffer=storageclass.from_address(ogre.CastInt(pointer)) posRGBA = 0 posRGB = 0 try: for j in range(height): for i in range( width ) : cbuffer[posRGBA]= ord(dataArray[posRGB]) # B posRGBA+=1 posRGB+=1 cbuffer[posRGBA]= ord(dataArray[posRGB]) # G posRGBA+=1 posRGB+=1 cbuffer[posRGBA]= ord(dataArray[posRGB]) # R posRGBA+=1 posRGB+=1 cbuffer[posRGBA]= 128 # A posRGBA+=1 posRGB += (WEBCAM_WIDTH - width)*3 # Unlock the pixel buffer pixelBuffer.unlock() except: print "Error during buffer fill at position ",posRGB return False return True if __name__=='__main__': ta = textureTest() feed = MyFeed("test") feed.start() try: ta.go() except: print "Application stopping" traceback.print_exc(file=sys.stdout) feed.stop()
TODO
In order to speed up things:
- Implement advice from Dynamic Textures:
- set up arrays in advance instead of creating them every frame,
- use Numeric Python to handle them.
- Make one buffer in the webcam thread, so that the next frame is ready when waitPix becomes True.
