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