help: Python video capture on MS Windows
a at b.com
a at b.com
Sun Dec 29 23:07:44 EST 2002
Happy New Year all!
I need assisance with the MS video captre libraries and Python...
I am writing an astronomy app (below) using videocapture.py where
the goal is to capture one particular frame from the camera.
I have made significant progress, but the sticking point seems
to be the random success rate of completely grabbing the particular frame.
I use a PII 350 with a Hauppage card and capture the image
buffer directly to an array in memory using the MS video capture
library calls in videocapture.py and wxPython. I.e.,
buffer, width, height = self.getBuffer()
after doing a
dev.displaycapturefilterproperties()
etc.
The problem seems to be one of initial synch: the image is never
shifted, but is often only 1/4 or 1/2 image with the rest being black.
It also seems to capture just fine out of live video if not
blocking/integrating the fields with the device's relays, only the
first frame buffer captured after enabling the CCD pin has the
problem, so it seems to "get back in synch".
(Does anyone know of an available 8 bit b/w driver for MS video?)
I added a function: getImageBuffer() to the videocapture.py
module to simply return the buffer as quickly as possible and
append to an array. (For some reason, getBuffer() itself
only captures ~21 fps, while
def getImageBuffer(self):
buffer, width, height = self.getBuffer()
return buffer
captures ~24fps.)
After shutting off then re-enabling the vertical clock on the
camera, the next frame output by the camera is very bright
since the charges have been integrating on the chip,
however, when I call:
images.append(self.cam.getImageBuffer())
it seems that it often appears 'out of synch'; with the top or
bottom third of the image being black - although in the correct
position (not scrolled). A similar C++/MS program by Jon Grove
does not have any errors when capturing the bright frames,
whereas mine is only about 50% full, correct frames.
Previous calls to getImageBuffer() return black frames, as
expected, and later calls seem more reliable and produce
good images.
Lower resolutions capture 50+fps, but still sometimes have
problems with the first frame containing bad data.
Do you know if the MS libraries establish some sort of synch
by watching the stream?
Any ideas for improvement?
Thanks,
Ray
PS, some images, etc are at:
http://rjs.org/astro/1004x
I have an old project at Sourceforge
http://sourceforge.net/projects/displayindexes/
and would like to make this a new one, if I can make the capture reliable.
=================================================
# capture.py
from wxPython.wx import *
from wxPython.grid import *
from winioport import *
import time, struct, sys
from VideoCapture import Device
import numarray
import array
from PIL import Image, ImageFont, ImageDraw
## change check to be performed every 1000 Python virtual instructions
sys.setcheckinterval(1000)
bitdict = {'0':'0000','1':'0001','2':'0010','3':'0011','4':'0100',
'5':'0101','6':'0110','7':'0111','8':'1000','9':'1001',
'a':'1010','b':'1011','c':'1100','d':'1101','e':'1110',
'f':'1111'}
def pportInData():
return bitdict[str(pportInp())]
def pportInDataBit(bit):
if hex(pportInp()) > 0x15:
allBits = str(hex(pportInp() - 240))[2]
else:
allBits = str(hex(pportInp()))[2]
bits = bitdict[str(allBits)]
return bits[3-bit]
ID_EXIT = 109
############################# Class defs #############################
class MainFrame(wxFrame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(680,600))
self.CreateStatusBar()
self.SetStatusText("")
self.tb = self.CreateToolBar(wxTB_HORIZONTAL|wxNO_BORDER|wxTB_3DBUTTONS)
self.tb.AddSimpleTool(100, wxBitmap('imagery\\go.bmp',
wxBITMAP_TYPE_BMP), "Go!", "Do an exposure")
EVT_TOOL(self, 100, self.OnExpose)
self.tb.AddSeparator()
self.tb.AddSimpleTool(10, wxBitmap('imagery\\ppl.bmp',
wxBITMAP_TYPE_BMP), "Toggle Binning", "Toggle D0 for pin 2 grounding - disable shift register")
EVT_TOOL(self, 10, self.OnPortD0)
self.tb.AddSimpleTool(20, wxBitmap('imagery\\go.bmp',
wxBITMAP_TYPE_BMP), "Exposure", "Toggle D1 for pin 5 - start long exposure")
EVT_TOOL(self, 20, self.OnPortD1)
self.tb.AddSimpleTool(30, wxBitmap('imagery\\black.bmp',
wxBITMAP_TYPE_BMP), "Toggle Amp ", "Toggle the CCD amplifier")
EVT_TOOL(self, 30, self.OnPortD2)
self.tb.AddSeparator()
self.tb.AddSimpleTool(40, wxBitmap('imagery\\grey.bmp',
wxBITMAP_TYPE_BMP), "Capture Properties", "Display the Capture Filter Properties")
EVT_TOOL(self, 40, self.OnCamProperties)
self.tb.Realize()
##### create and add notebook pages ###############################
self.nb = wxNotebook(self, -1)
self.nb.Grid = wxGrid(self.nb, -1)
self.nb.Grid.CreateGrid(14, 2)
self.nb.AddPage(self.nb.Grid, "Data")
self.nb.Grid.SetCellValue(0,0,'Exposure')
self.nb.Grid.SetCellValue(0,1,'.033')
self.nb.Grid.SetCellValue(1,0,'min-max value')
self.nb.Grid.SetCellValue(1,1,'90')
self.nb.Grid.SetCellValue(2,0,'Amp disable')
self.nb.Grid.SetCellValue(2,1,'n')
self.nb.Grid.SetCellValue(3,0,'Amp power-up')
self.nb.Grid.SetCellValue(3,1,'1.0')
self.nb.Grid.SetCellValue(4,0,'Show frames')
self.nb.Grid.SetCellValue(4,1,'0')
self.nb.Grid.SetCellValue(5,0,'0x378')
self.nb.Grid.SetCellValue(5,1,str(inp(0x378)))
self.nb.Grid.SetCellValue(6,0,'D0 - 0x378')
self.nb.Grid.SetCellValue(7,0,'D1 - 0x378')
self.nb.Grid.SetCellValue(8,0,'D2 - 0x378')
self.nb.Grid.SetCellValue(6,1,pportInDataBit(0))
self.nb.Grid.SetCellValue(7,1,pportInDataBit(1))
self.nb.Grid.SetCellValue(8,1,pportInDataBit(2))
self.nb.Grid.SetCellValue(9,0,'')
self.nb.Grid.SetCellValue(9,1,'')
self.nb.Grid.SetCellValue(10,0,'# of sequence')
self.nb.Grid.SetCellValue(10,1,'3')
self.nb.Grid.SetCellValue(11,0,'')
self.nb.Grid.SetCellValue(11,1,'')
self.nb.Grid.SetCellValue(12,0,'')
self.nb.Grid.SetCellValue(12,1,'')
self.nb.Grid.SetCellValue(13,0,'Image display?')
self.nb.Grid.SetCellValue(13,1,'n')
self.nb.blendedWindow = wxScrolledWindow(self.nb, -1, (-1,-1), (-1,-1), style = wxHSCROLL | wxVSCROLL)
self.nb.blendedWindow.SetScrollbars(10, 10, 150, 150, xPos = 0, yPos = 0,noRefresh = FALSE)
self.nb.AddPage(self.nb.blendedWindow, "combined image")
self.nb.SetSelection(0)
self.cam = Device(devnum=0, showVideoWindow=0)
def OnExpose(self, event):
## use the first video-device which is found
## devnum=1 uses the second one and so on
#cam = Device(devnum=0, showVideoWindow=0)
#cam.displayCaptureFilterProperties()
buffer, width, height = self.cam.getBuffer()
if(self.nb.GetPageCount() > 2):
while (self.nb.GetPageCount() > 1):
self.nb.DeletePage(self.nb.GetPageCount()-1)
self.nb.blendedWindow = wxScrolledWindow(self.nb, -1, (-1,-1), (-1,-1), style = wxHSCROLL | wxVSCROLL)
self.nb.blendedWindow.SetScrollbars(10, 10, 150, 150, xPos = 0, yPos = 0,noRefresh = FALSE)
self.nb.AddPage(self.nb.blendedWindow, "combined image")
self.nb.SetSelection(1)
## begin the sequence
merged = 0
for seqNum in range(int(self.nb.Grid.GetCellValue(10, 1))):
self.SetStatusText("Exposing image "+str(seqNum)+" of "+self.nb.Grid.GetCellValue(10, 1))
images = []*3
#feilds = numarray.zeros((921600, 1), Int16)
## open pin 5, start integrating
alterDataBit(1,1)
## ground the shift register, pin 2
alterDataBit(0,1)
if (self.nb.Grid.GetCellValue(2, 1)=='y'):
## turn amp off?, open pin 9
alterDataBit(2,1)
## start capture time, correct for the amp stabilizing time
time.sleep(float(self.nb.Grid.GetCellValue(0, 1))-float(self.nb.Grid.GetCellValue(3, 1)))
## close pin 9, enable the amplifier
alterDataBit(2,0)
## wait for voltage to stabilize
time.sleep(float(self.nb.Grid.GetCellValue(3, 1)))
else:
## start capture time
time.sleep(float(self.nb.Grid.GetCellValue(0, 1)))
#images.append(self.cam.getImageBuffer())
## close pin 5
alterDataBit(1,0)
images.append(self.cam.getImageBuffer())
#time.sleep(1) ## for testing movement
images.append(self.cam.getImageBuffer())
images.append(self.cam.getImageBuffer())
images.append(self.cam.getImageBuffer())
## open pin 2
alterDataBit(0,0)
images.append(self.cam.getImageBuffer())
images.append(self.cam.getImageBuffer())
images.append(self.cam.getImageBuffer())
images.append(self.cam.getImageBuffer())
if (len(images)):
#print 'captured', n
found1 = FALSE
for n in range(4):
#fld = array.array('c', images[n])
#fld1 = fld.tolist()
feild = numarray.fromstring(images[n], numarray.Int8).astype(numarray.Int32)[::3]
print numarray.sum(feild)/(640*480)
im1 = Image.fromstring('RGB', (width, height), images[n], 'raw', 'RGB', 0, -1)
extremes = im1.getextrema()
if(self.nb.Grid.GetCellValue(13, 1)=='y'): print extremes[0][1]
if extremes[0][1] > float(self.nb.Grid.GetCellValue(1, 1)):
if(self.nb.Grid.GetCellValue(13, 1)=='y'): print 'frame1:', n
found1 = TRUE
break
if (found1):
## show frames?
if(int(self.nb.Grid.GetCellValue(4, 1))):
im1.transpose(Image.FLIP_TOP_BOTTOM)
self.nb.imgWindow = wxScrolledWindow(self.nb, -1, (-1,-1), (-1,-1), style = wxHSCROLL | wxVSCROLL)
self.nb.imgWindow.SetScrollbars(10, 10, 150, 150, xPos = 0, yPos = 0,noRefresh = FALSE)
self.nb.AddPage(self.nb.imgWindow, "frame 1")
displayImage = wxEmptyImage(width,height)
displayImage.SetData(im1.convert("RGB").tostring())
bmp = wxBitmapFromImage(displayImage)
wxStaticBitmap(self.nb.imgWindow, -1, bmp, wxPoint(0, 0),
wxSize(bmp.GetWidth(), bmp.GetHeight()))
found2 = FALSE
for m in range(4, len(images)):
im2 = Image.fromstring('RGB', (width, height), images[m], 'raw', 'RGB', 0, -1)
extremes = im2.getextrema()
if(self.nb.Grid.GetCellValue(13, 1)=='y'): print extremes[0][1]
if extremes[0][1] > float(self.nb.Grid.GetCellValue(1, 1)):
if(self.nb.Grid.GetCellValue(13, 1)=='y'): print 'frame2:', m
found2 = TRUE
break
if (found2):
## show frames?
if(int(self.nb.Grid.GetCellValue(4, 1))):
im2.transpose(Image.FLIP_TOP_BOTTOM)
self.nb.imgWindow = wxScrolledWindow(self.nb, -1, (-1,-1), (-1,-1), style = wxHSCROLL | wxVSCROLL)
self.nb.imgWindow.SetScrollbars(10, 10, 150, 150, xPos = 0, yPos = 0,noRefresh = FALSE)
self.nb.AddPage(self.nb.imgWindow, "frame 2")
displayImage = wxEmptyImage(width,height)
displayImage.SetData(im2.convert("RGB").tostring())
bmp = wxBitmapFromImage(displayImage)
wxStaticBitmap(self.nb.imgWindow, -1, bmp, wxPoint(0, 0),
wxSize(bmp.GetWidth(), bmp.GetHeight()))
if (found1 and found2):
images.append('')
## combine the feilds
size = width*height*3
lineDataLen = width*3
last = len(images)-1
line = 0
lineArray = [None]*height
while (line < height):
lineArray[line] = images[n][line*lineDataLen:(line+1)*lineDataLen]
lineArray[line+1] = images[m][(line+1)*lineDataLen:(line+2)*lineDataLen]
line += 2
images[last] = "".join(lineArray)
im3 = Image.fromstring('RGB', (width, height), images[len(images)-1], 'raw', 'RGB', 0, -1)
if(self.nb.Grid.GetCellValue(13, 1)=='y'):
self.nb.imgWindow = wxScrolledWindow(self.nb, -1, (-1,-1), (-1,-1), style = wxHSCROLL | wxVSCROLL)
self.nb.imgWindow.SetScrollbars(10, 10, 150, 150, xPos = 0, yPos = 0,noRefresh = FALSE)
self.nb.AddPage(self.nb.imgWindow, string.join(["image", str(seqNum)]))
displayImage = wxEmptyImage(width,height)
displayImage.SetData(im3.convert("RGB").tostring())
bmp = wxBitmapFromImage(displayImage)
wxStaticBitmap(self.nb.imgWindow, -1, bmp, wxPoint(0, 0),
wxSize(bmp.GetWidth(), bmp.GetHeight()))
self.nb.AdvanceSelection()
if (seqNum==0):
## use the first image as the base
blendedImage = im3
else:
blendedImage = Image.blend(blendedImage, im3, 0.5)
merged += 1
else:
print len(images), 'images captured'
self.nb.Grid.SetCellValue(5,1,pportInData())
if (merged):
finalDisplayImage = wxEmptyImage(width,height)
finalDisplayImage.SetData(blendedImage.convert("RGB").tostring())
bmp = wxBitmapFromImage(finalDisplayImage)
self.wxbmp = wxStaticBitmap(self.nb.blendedWindow, -1, bmp, wxPoint(0, 0),
wxSize(bmp.GetWidth(), bmp.GetHeight()))
self.nb.SetSelection(1)
#blendedImage.save("blendedImage.jpg", "JPEG")
#print len(blendedImage.histogram())
#print blendedImage.histogram()
def OnPortD0(self, event):
invertDataBit(0)
self.nb.Grid.SetCellValue(6,1,pportInDataBit(0))
self.nb.Grid.SetCellValue(5,1,pportInData())
def OnPortD1(self, event):
invertDataBit(1)
self.nb.Grid.SetCellValue(7,1,pportInDataBit(1))
self.nb.Grid.SetCellValue(5,1,pportInData())
def OnPortD2(self, event):
invertDataBit(2)
self.nb.Grid.SetCellValue(8,1,pportInDataBit(2))
self.nb.Grid.SetCellValue(5,1,pportInData())
def OnCamProperties(self, event):
#cam = Device(devnum=0, showVideoWindow=0)
self.cam.displayCaptureFilterProperties()
############### Main application class ###########
class AstroApp(wxApp):
def OnInit(self):
frame = MainFrame(NULL, -1, "Hydra")
frame.Show(true)
self.SetTopWindow(frame)
return true
app = AstroApp(0)
app.MainLoop()
More information about the Python-list
mailing list