Commit 5e044b70 authored by Guido van Rossum's avatar Guido van Rossum

- Separated grabbing (which isn't used much!) from VFile.

- Renamed old Vcopy.py to OldVcopy.py, some cosmetic changes to it (is
  it still needed?)
- Added new Vcopy.py which does everything that Vtime.py does but also
  format conversions, image scaling, and packfactors.
- VFile: make packfactor always a tuple; introduce set and get methods
  for pf, format, and calculate some derived values.
- Added new module GET.py to std library, use it instead of defining
  DM* in VFile.
- Get rid of C programs (new Python programs can do all that they do
  and they probably don't understand the current file format anyway).
parent b616ebe4
...@@ -59,9 +59,9 @@ Vtime.py Copy a video file, manipulating the time codes (e.g. ...@@ -59,9 +59,9 @@ Vtime.py Copy a video file, manipulating the time codes (e.g.
faster/slower, or regenerate time codes, or drop faster/slower, or regenerate time codes, or drop
frames too close apart) frames too close apart)
Vcopy.py selectively write frames from one movie file to another Vcopy.py Universal video file copying tool. Can manipulate the
usage: Vcopy [-t type] [-m treshold] [-a] infile outfile time codes, change the type, size, and packfactor.
commands: 'n' gets next frame; 'w' writes current frame Subsumes Vtime.py.
Vmkjpeg.py compress an rgb or grey video file to jpeg[grey] format Vmkjpeg.py compress an rgb or grey video file to jpeg[grey] format
......
...@@ -6,11 +6,10 @@ ...@@ -6,11 +6,10 @@
# #
# VideoParams: maintain essential parameters of a video file # VideoParams: maintain essential parameters of a video file
# Displayer: display a frame in a window (with some extra parameters) # Displayer: display a frame in a window (with some extra parameters)
# Grabber: grab a frame from a window
# BasicVinFile: read a CMIF video file # BasicVinFile: read a CMIF video file
# BasicVoutFile: write a CMIF video file # BasicVoutFile: write a CMIF video file
# VinFile: BasicVinFile + Displayer # VinFile: BasicVinFile + Displayer
# VoutFile: BasicVoutFile + Displayer + Grabber # VoutFile: BasicVoutFile + Displayer
# #
# XXX Future extension: # XXX Future extension:
# BasicVinoutFile: supports overwriting of individual frames # BasicVinoutFile: supports overwriting of individual frames
...@@ -21,6 +20,7 @@ ...@@ -21,6 +20,7 @@
import sys import sys
import gl import gl
import GL import GL
import GET
import colorsys import colorsys
import imageop import imageop
...@@ -32,14 +32,6 @@ CallError = 'VFile.CallError' # bad call ...@@ -32,14 +32,6 @@ CallError = 'VFile.CallError' # bad call
AssertError = 'VFile.AssertError' # internal malfunction AssertError = 'VFile.AssertError' # internal malfunction
# Constants returned by gl.getdisplaymode(), from <gl/get.h>
DMRGB = 0
DMSINGLE = 1
DMDOUBLE = 2
DMRGBDOUBLE = 5
# Max nr. of colormap entries to use # Max nr. of colormap entries to use
MAXMAP = 4096 - 256 MAXMAP = 4096 - 256
...@@ -151,9 +143,9 @@ def is_entry_indigo(): ...@@ -151,9 +143,9 @@ def is_entry_indigo():
b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE) b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
return (r, g, b) == (3, 3, 2) return (r, g, b) == (3, 3, 2)
#
# Predicate function to see whether this machine supports pixmode(PM_SIZE) # Predicate to see whether this machine supports pixmode(PM_SIZE) with
# with values 1 or 4. # values 1 or 4.
# #
# XXX Temporarily disabled, since it is unclear which machines support # XXX Temporarily disabled, since it is unclear which machines support
# XXX which pixelsizes. # XXX which pixelsizes.
...@@ -161,66 +153,24 @@ def is_entry_indigo(): ...@@ -161,66 +153,24 @@ def is_entry_indigo():
# XXX The XS appears to support 4 bit pixels, but (looking at osview) it # XXX The XS appears to support 4 bit pixels, but (looking at osview) it
# XXX seems as if the conversion is done by the kernel (unpacking ourselves # XXX seems as if the conversion is done by the kernel (unpacking ourselves
# XXX is faster than using PM_SIZE=4) # XXX is faster than using PM_SIZE=4)
#
def support_packed_pixels(): def support_packed_pixels():
return 0 # To be architecture-dependent return 0 # To be architecture-dependent
# Routines to grab data, per color system (only a few really supported).
# (These functions are used via eval with a constructed argument!)
def grab_rgb(w, h, pf):
if gl.getdisplaymode() <> DMRGB:
raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
if pf <> 1 and pf <> 0:
raise Error, 'Sorry, only grab rgb with packfactor 1'
return gl.lrectread(0, 0, w-1, h-1), None
def grab_rgb8(w, h, pf):
if gl.getdisplaymode() <> DMRGB:
raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode'
if pf <> 1 and pf <> 0:
raise Error, 'Sorry, can only grab rgb8 with packfactor 1'
if not is_entry_indigo():
raise Error, 'Sorry, can only grab rgb8 on entry level Indigo'
# XXX Dirty Dirty here.
# XXX Set buffer to cmap mode, grab image and set it back.
gl.cmode()
gl.gconfig()
gl.pixmode(GL.PM_SIZE, 8)
data = gl.lrectread(0, 0, w-1, h-1)
data = data[:w*h] # BUG FIX for python lrectread
gl.RGBmode()
gl.gconfig()
gl.pixmode(GL.PM_SIZE, 32)
return data, None
def grab_grey(w, h, pf):
raise Error, 'Sorry, grabbing grey not implemented'
def grab_yiq(w, h, pf):
raise Error, 'Sorry, grabbing yiq not implemented'
def grab_hls(w, h, pf):
raise Error, 'Sorry, grabbing hls not implemented'
def grab_hsv(w, h, pf):
raise Error, 'Sorry, grabbing hsv not implemented'
def grab_jpeg(w, h, pf):
# XXX Ought to grab rgb and compress it
raise Error, 'sorry, grabbing jpeg not implemented'
def grab_jpeggrey(w, h, pf):
raise Error, 'sorry, grabbing jpeggrey not implemented'
# Tables listing bits per pixel for some formats
# Choose one of the above based upon a color system name bitsperpixel = { \
'rgb': 32, \
'rgb8': 8, \
'grey': 8, \
'grey4': 4, \
'grey2': 2, \
'mono': 1, \
}
def choose_grabber(format): bppafterdecomp = {'jpeg': 32, 'jpeggrey': 8}
try:
return eval('grab_' + format)
except:
raise Error, 'Unknown color system: ' + `format`
# Base class to manage video format parameters # Base class to manage video format parameters
...@@ -233,39 +183,102 @@ class VideoParams: ...@@ -233,39 +183,102 @@ class VideoParams:
def init(self): def init(self):
# Essential parameters # Essential parameters
self.frozen = 0 # if set, can't change parameters
self.format = 'grey' # color system used self.format = 'grey' # color system used
# Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey, # Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey,
# mono, grey2, grey4 # mono, grey2, grey4
self.width = 0 # width of frame self.width = 0 # width of frame
self.height = 0 # height of frame self.height = 0 # height of frame
self.packfactor = 1 # expansion using rectzoom self.packfactor = 1, 1 # expansion using rectzoom
# if packfactor == 0, data is one 32-bit word/pixel;
# otherwise, data is one byte/pixel
self.c0bits = 8 # bits in first color dimension self.c0bits = 8 # bits in first color dimension
self.c1bits = 0 # bits in second color dimension self.c1bits = 0 # bits in second color dimension
self.c2bits = 0 # bits in third color dimension self.c2bits = 0 # bits in third color dimension
self.offset = 0 # colormap index offset (XXX ???) self.offset = 0 # colormap index offset (XXX ???)
self.chrompack = 0 # set if separate chrominance data self.chrompack = 0 # set if separate chrominance data
self.setderived()
return self return self
# Freeze the parameters (disallow changes)
def freeze(self):
self.frozen = 1
# Unfreeze the parameters (allow changes)
def unfreeze(self):
self.frozen = 0
# Set some values derived from the standard info values
def setderived(self):
if self.frozen: raise AssertError
if bitsperpixel.has_key(self.format):
self.bpp = bitsperpixel[self.format]
else:
self.bpp = 0
xpf, ypf = self.packfactor
self.xpf = abs(xpf)
self.ypf = abs(ypf)
self.mirror_image = (xpf < 0)
self.upside_down = (ypf < 0)
self.realwidth = self.width / self.xpf
self.realheight = self.height / self.ypf
# Set the frame width and height (e.g. from gl.getsize()) # Set the frame width and height (e.g. from gl.getsize())
def setsize(self, width, height): def setsize(self, width, height):
if self.frozen: raise CallError
width = (width/self.xpf)*self.xpf
height = (height/self.ypf)*self.ypf
self.width, self.height = width, height self.width, self.height = width, height
self.setderived()
# Retrieve the frame width and height (e.g. for gl.prefsize()) # Retrieve the frame width and height (e.g. for gl.prefsize())
def getsize(self): def getsize(self):
return (self.width, self.height) return (self.width, self.height)
# Set all parameters. # Set the format
# This does limited validity checking;
# if the check fails no parameters are changed def setformat(self, format):
if self.frozen: raise CallError
if format <> self.format:
self.format = format
self.setderived()
# Get the format
def getformat(self):
return self.format
# Set the packfactor
def setpf(self, pf):
if self.frozen: raise CallError
## if type(pf) is type(0):
## if pf == 0:
## pf = (1, 1)
## else:
## pf = (pf, pf)
if type(pf) is not type(()) or len(pf) <> 2: raise CallError
self.packfactor = pf
self.setderived()
# Get the packfactor
def getpf(self):
return self.packfactor
# Set all parameters
def setinfo(self, values): def setinfo(self, values):
(self.format, self.width, self.height, self.packfactor,\ if self.frozen: raise CallError
self.c0bits, self.c1bits, self.c2bits, self.offset, \ self.setformat(values[0])
self.chrompack) = values self.setpf(values[3])
self.setsize(values[1], values[2])
(self.c0bits, self.c1bits, self.c2bits, \
self.offset, self.chrompack) = values[4:]
self.setderived()
# Retrieve all parameters in a format suitable for a subsequent # Retrieve all parameters in a format suitable for a subsequent
# call to setinfo() # call to setinfo()
...@@ -281,27 +294,17 @@ class VideoParams: ...@@ -281,27 +294,17 @@ class VideoParams:
print 'Format: ', self.format print 'Format: ', self.format
print 'Size: ', self.width, 'x', self.height print 'Size: ', self.width, 'x', self.height
print 'Pack: ', self.packfactor, '; chrom:', self.chrompack print 'Pack: ', self.packfactor, '; chrom:', self.chrompack
print 'Bpp: ', self.bpp
print 'Bits: ', self.c0bits, self.c1bits, self.c2bits print 'Bits: ', self.c0bits, self.c1bits, self.c2bits
print 'Offset: ', self.offset print 'Offset: ', self.offset
# Calculate data size, if possible # Calculate data size, if possible
# (Not counting frame header or cdata size)
def calcframesize(self): def calcframesize(self):
if self.format == 'rgb': if not self.bpp: raise CallError
return self.width*self.height*4 size = self.width/self.xpf * self.height/self.ypf
if self.format in ('jpeg', 'jpeggrey'): size = (size * self.bpp + 7) / 8
raise CallError
if type(self.packfactor) == type(()):
xpf, ypf = self.packfactor
else:
xpf = ypf = self.packfactor
if ypf < 0: ypf = -ypf
size = (self.width/xpf)*(self.height/ypf)
if self.format == 'grey4':
size = (size+1)/2
elif self.format == 'grey2':
size = (size+3)/4
elif self.format == 'mono':
size = (size+7)/8
return size return size
...@@ -346,33 +349,16 @@ class Displayer(VideoParams): ...@@ -346,33 +349,16 @@ class Displayer(VideoParams):
(0,0,self.width,self.height)) (0,0,self.width,self.height))
def showpartframe(self, data, chromdata, (x,y,w,h)): def showpartframe(self, data, chromdata, (x,y,w,h)):
pf = self.packfactor pmsize = self.bpp
pmsize = 8 xpf, ypf = self.xpf, self.ypf
if pf: if self.upside_down:
if type(pf) == type(()): gl.pixmode(GL.PM_TTOB, 1)
xpf, ypf = pf if self.mirror_image:
else: gp.pixmode(GL.PM_RTOL, 1)
xpf = ypf = pf
if ypf < 0:
gl.pixmode(GL.PM_TTOB, 1)
ypf = -ypf
if xpf < 0:
gl.pixmode(GL.PM_RTOL, 1)
xpf = -xpf
else:
xpf = ypf = 1
if self.format in ('jpeg', 'jpeggrey'): if self.format in ('jpeg', 'jpeggrey'):
import jpeg import jpeg
data, width, height, bytes = jpeg.decompress(data) data, width, height, bytes = jpeg.decompress(data)
if self.format == 'jpeg': pmsize = bytes*8
b = 4
xp = yp = 1
else:
b = 1
xp = xpf
yp = ypf
if (width, height, bytes) <> (w/xp, h/yp, b):
raise Error, 'jpeg data has wrong size'
elif self.format in ('mono', 'grey4'): elif self.format in ('mono', 'grey4'):
if self.mustunpack: if self.mustunpack:
if self.format == 'mono': if self.format == 'mono':
...@@ -381,24 +367,18 @@ class Displayer(VideoParams): ...@@ -381,24 +367,18 @@ class Displayer(VideoParams):
elif self.format == 'grey4': elif self.format == 'grey4':
data = imageop.grey42grey(data, \ data = imageop.grey42grey(data, \
w/xpf, h/ypf) w/xpf, h/ypf)
else: pmsize = 8
# We don't need to unpack, the hardware
# can do it.
if self.format == 'mono':
pmsize = 1
else:
pmsize = 4
elif self.format == 'grey2': elif self.format == 'grey2':
data = imageop.grey22grey(data, w/xpf, h/ypf) data = imageop.grey22grey(data, w/xpf, h/ypf)
pmsize = 8
if not self.colormapinited: if not self.colormapinited:
self.initcolormap() self.initcolormap()
if self.fixcolor0: if self.fixcolor0:
gl.mapcolor(self.color0) gl.mapcolor(self.color0)
self.fixcolor0 = 0 self.fixcolor0 = 0
xfactor = yfactor = self.magnify xfactor = yfactor = self.magnify
if pf: xfactor = xfactor * xpf
xfactor = xfactor * xpf yfactor = yfactor * ypf
yfactor = yfactor * ypf
if chromdata and not self.skipchrom: if chromdata and not self.skipchrom:
cp = self.chrompack cp = self.chrompack
cx = int(x*xfactor*cp) + self.xorigin cx = int(x*xfactor*cp) + self.xorigin
...@@ -411,13 +391,13 @@ class Displayer(VideoParams): ...@@ -411,13 +391,13 @@ class Displayer(VideoParams):
gl.lrectwrite(cx, cy, cx + cw - 1, cy + ch - 1, \ gl.lrectwrite(cx, cy, cx + cw - 1, cy + ch - 1, \
chromdata) chromdata)
# #
if pf: if pmsize < 32:
gl.writemask((1 << self.c0bits) - 1) gl.writemask((1 << self.c0bits) - 1)
gl.pixmode(GL.PM_SIZE, pmsize) gl.pixmode(GL.PM_SIZE, pmsize)
w = w/xpf w = w/xpf
h = h/ypf h = h/ypf
x = x/xpf x = x/xpf
y = y/ypf y = y/ypf
gl.rectzoom(xfactor, yfactor) gl.rectzoom(xfactor, yfactor)
x = int(x*xfactor)+self.xorigin x = int(x*xfactor)+self.xorigin
y = int(y*yfactor)+self.yorigin y = int(y*yfactor)+self.yorigin
...@@ -464,7 +444,7 @@ class Displayer(VideoParams): ...@@ -464,7 +444,7 @@ class Displayer(VideoParams):
def clear(self): def clear(self):
if not self.colormapinited: raise CallError if not self.colormapinited: raise CallError
if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE): if gl.getdisplaymode() in (GET.DMRGB, GET.DMRGBDOUBLE):
gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.RGBcolor(200, 200, 200) # XXX rather light grey
gl.clear() gl.clear()
return return
...@@ -477,7 +457,7 @@ class Displayer(VideoParams): ...@@ -477,7 +457,7 @@ class Displayer(VideoParams):
def clearto(self, r, g, b): def clearto(self, r, g, b):
if not self.colormapinited: raise CallError if not self.colormapinited: raise CallError
if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE): if gl.getdisplaymode() in (GET.DMRGB, GET.DMRGBDOUBLE):
gl.RGBcolor(r, g, b) gl.RGBcolor(r, g, b)
gl.clear() gl.clear()
return return
...@@ -553,23 +533,6 @@ class Displayer(VideoParams): ...@@ -553,23 +533,6 @@ class Displayer(VideoParams):
gl.gflush() # send the colormap changes to the X server gl.gflush() # send the colormap changes to the X server
# Class to grab frames from a window.
# (This has fewer user-settable parameters than Displayer.)
# It is the caller's responsibility to initialize the window and to
# ensure that it is current when using grabframe()
class Grabber(VideoParams):
# XXX The init() method of VideoParams is just fine, for now
# Grab a frame.
# Return (data, chromdata) just like getnextframe().
def grabframe(self):
grabber = choose_grabber(self.format)
return grabber(self.width, self.height, self.packfactor)
# Read a CMIF video file header. # Read a CMIF video file header.
# Return (version, values) where version is 0.0, 1.0, 2.0 or 3.[01], # Return (version, values) where version is 0.0, 1.0, 2.0 or 3.[01],
# and values is ready for setinfo(). # and values is ready for setinfo().
...@@ -620,8 +583,6 @@ def readfileheader(fp, filename): ...@@ -620,8 +583,6 @@ def readfileheader(fp, filename):
format, rest = eval(line[:-1]) format, rest = eval(line[:-1])
except: except:
raise Error, filename + ': Bad 3.[01] color info' raise Error, filename + ': Bad 3.[01] color info'
if format == 'xrgb8':
format = 'rgb8' # rgb8 upside-down, for X
if format in ('rgb', 'jpeg'): if format in ('rgb', 'jpeg'):
c0bits = c1bits = c2bits = 0 c0bits = c1bits = c2bits = 0
chrompack = 0 chrompack = 0
...@@ -637,6 +598,11 @@ def readfileheader(fp, filename): ...@@ -637,6 +598,11 @@ def readfileheader(fp, filename):
c0bits, c1bits, c2bits, chrompack, offset = rest c0bits, c1bits, c2bits, chrompack, offset = rest
except: except:
raise Error, filename + ': Bad 3.[01] color info' raise Error, filename + ': Bad 3.[01] color info'
if format == 'xrgb8':
format = 'rgb8' # rgb8 upside-down, for X
upside_down = 1
else:
upside_down = 0
# #
# Get frame geometry info # Get frame geometry info
# #
...@@ -657,15 +623,18 @@ def readfileheader(fp, filename): ...@@ -657,15 +623,18 @@ def readfileheader(fp, filename):
packfactor = 2 packfactor = 2
else: else:
raise Error, filename + ': Bad (w,h,pf) info' raise Error, filename + ': Bad (w,h,pf) info'
if type(packfactor) == type(()): if type(packfactor) is type(0):
if packfactor == 0: packfactor = 1
xpf = ypf = packfactor
else:
xpf, ypf = packfactor xpf, ypf = packfactor
xpf = abs(xpf) if upside_down:
ypf = abs(ypf) ypf = -ypf
width = (width/xpf) * xpf packfactor = (xpf, ypf)
height = (height/ypf) * ypf xpf = abs(xpf)
elif packfactor > 1: ypf = abs(ypf)
width = (width / packfactor) * packfactor width = (width/xpf) * xpf
height = (height / packfactor) * packfactor height = (height/ypf) * ypf
# #
# Return (version, values) # Return (version, values)
# #
...@@ -762,7 +731,8 @@ class BasicVinFile(VideoParams): ...@@ -762,7 +731,8 @@ class BasicVinFile(VideoParams):
self.fp = fp self.fp = fp
self.filename = filename self.filename = filename
self.version, values = readfileheader(fp, filename) self.version, values = readfileheader(fp, filename)
VideoParams.setinfo(self, values) self.setinfo(values)
self.freeze()
if self.version == 0.0: if self.version == 0.0:
w, h, pf = self.width, self.height, self.packfactor w, h, pf = self.width, self.height, self.packfactor
if pf == 0: if pf == 0:
...@@ -801,12 +771,6 @@ class BasicVinFile(VideoParams): ...@@ -801,12 +771,6 @@ class BasicVinFile(VideoParams):
del self.fp del self.fp
del self._readframeheader del self._readframeheader
def setinfo(self, values):
raise CallError # Can't change info of input file!
def setsize(self, width, height):
raise CallError # Can't change info of input file!
def rewind(self): def rewind(self):
if not self.canseek: if not self.canseek:
raise Error, self.filename + ': can\'t seek' raise Error, self.filename + ': can\'t seek'
...@@ -1022,8 +986,7 @@ class BasicVoutFile(VideoParams): ...@@ -1022,8 +986,7 @@ class BasicVoutFile(VideoParams):
self = VideoParams.init(self) self = VideoParams.init(self)
self.fp = fp self.fp = fp
self.filename = filename self.filename = filename
self.version = 3.0 # In case anyone inquries self.version = 3.1 # In case anyone inquries
self.headerwritten = 0
return self return self
def flush(self): def flush(self):
...@@ -1034,27 +997,23 @@ class BasicVoutFile(VideoParams): ...@@ -1034,27 +997,23 @@ class BasicVoutFile(VideoParams):
del self.fp del self.fp
def prealloc(self, nframes): def prealloc(self, nframes):
if not self.headerwritten: raise CallError if not self.frozen: raise CallError
data = '\xff' * self.calcframesize() data = '\xff' * (self.calcframesize() + 64)
pos = self.fp.tell() pos = self.fp.tell()
for i in range(nframes): for i in range(nframes):
self.fp.write(data) self.fp.write(data)
self.fp.seek(pos) self.fp.seek(pos)
def setinfo(self, values):
if self.headerwritten: raise CallError
VideoParams.setinfo(self, values)
def writeheader(self): def writeheader(self):
if self.headerwritten: raise CallError if self.frozen: raise CallError
writefileheader(self.fp, self.getinfo()) writefileheader(self.fp, self.getinfo())
self.headerwritten = 1 self.freeze()
self.atheader = 1 self.atheader = 1
self.framecount = 0 self.framecount = 0
def rewind(self): def rewind(self):
self.fp.seek(0) self.fp.seek(0)
self.headerwritten = 0 self.unfreeze()
self.atheader = 1 self.atheader = 1
self.framecount = 0 self.framecount = 0
...@@ -1071,7 +1030,7 @@ class BasicVoutFile(VideoParams): ...@@ -1071,7 +1030,7 @@ class BasicVoutFile(VideoParams):
self.writeframedata(data, cdata) self.writeframedata(data, cdata)
def writeframeheader(self, t, ds, cs): def writeframeheader(self, t, ds, cs):
if not self.headerwritten: self.writeheader() if not self.frozen: self.writeheader()
if not self.atheader: raise CallError if not self.atheader: raise CallError
data = `(t, ds, cs)` data = `(t, ds, cs)`
n = len(data) n = len(data)
...@@ -1080,14 +1039,14 @@ class BasicVoutFile(VideoParams): ...@@ -1080,14 +1039,14 @@ class BasicVoutFile(VideoParams):
self.atheader = 0 self.atheader = 0
def writeframedata(self, data, cdata): def writeframedata(self, data, cdata):
if not self.headerwritten or self.atheader: raise CallError if not self.frozen or self.atheader: raise CallError
if data: self.fp.write(data) if data: self.fp.write(data)
if cdata: self.fp.write(cdata) if cdata: self.fp.write(cdata)
self.atheader = 1 self.atheader = 1
self.framecount = self.framecount + 1 self.framecount = self.framecount + 1
# Classes that combine files with displayers and/or grabbers: # Classes that combine files with displayers:
class VinFile(RandomVinFile, Displayer): class VinFile(RandomVinFile, Displayer):
...@@ -1101,7 +1060,7 @@ class VinFile(RandomVinFile, Displayer): ...@@ -1101,7 +1060,7 @@ class VinFile(RandomVinFile, Displayer):
return t return t
class VoutFile(BasicVoutFile, Displayer, Grabber): class VoutFile(BasicVoutFile, Displayer):
def initfp(self, fp, filename): def initfp(self, fp, filename):
self = Displayer.init(self) self = Displayer.init(self)
......
# Class to grab frames from a window.
# (This has fewer user-settable parameters than Displayer.)
# It is the caller's responsibility to initialize the window and to
# ensure that it is current when using grabframe()
import gl, GL
import VFile
import GET
from VFile import Error
class VGrabber(VFile.VideoParams):
# XXX The init() method of VideoParams is just fine, for now
# Grab a frame.
# Return (data, chromdata) just like getnextframe().
def grabframe(self):
grabber = choose_grabber(self.format)
return grabber(self.width, self.height, self.packfactor)
# Choose one of the grabber functions below based upon a color system name
def choose_grabber(format):
try:
return eval('grab_' + format)
except:
raise Error, 'Unknown color system: ' + `format`
# Routines to grab data, per color system (only a few really supported).
# (These functions are used via eval with a constructed argument!)
def grab_rgb(w, h, pf):
if gl.getdisplaymode() <> GET.DMRGB:
raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
if pf <> (1, 1):
raise Error, 'Sorry, only grab rgb with packfactor (1,1)'
return gl.lrectread(0, 0, w-1, h-1), None
def grab_rgb8(w, h, pf):
if gl.getdisplaymode() <> GET.DMRGB:
raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode'
if pf <> (1, 1):
raise Error, 'Sorry, can only grab rgb8 with packfactor (1,1)'
if not VFile.is_entry_indigo():
raise Error, 'Sorry, can only grab rgb8 on entry level Indigo'
# XXX Dirty Dirty here.
# XXX Set buffer to cmap mode, grab image and set it back.
gl.cmode()
gl.gconfig()
gl.pixmode(GL.PM_SIZE, 8)
data = gl.lrectread(0, 0, w-1, h-1)
data = data[:w*h] # BUG FIX for python lrectread
gl.RGBmode()
gl.gconfig()
gl.pixmode(GL.PM_SIZE, 32)
return data, None
def grab_grey(w, h, pf):
raise Error, 'Sorry, grabbing grey not implemented'
def grab_yiq(w, h, pf):
raise Error, 'Sorry, grabbing yiq not implemented'
def grab_hls(w, h, pf):
raise Error, 'Sorry, grabbing hls not implemented'
def grab_hsv(w, h, pf):
raise Error, 'Sorry, grabbing hsv not implemented'
def grab_jpeg(w, h, pf):
# XXX Ought to grab rgb and compress it
raise Error, 'sorry, grabbing jpeg not implemented'
def grab_jpeggrey(w, h, pf):
raise Error, 'sorry, grabbing jpeggrey not implemented'
# Copy a video file, interactively, frame-by-frame. #! /ufs/guido/bin/sgi/python
# Universal (non-interactive) CMIF video file copier.
# Possibilities:
#
# - Manipulate the time base:
# = resample at a fixed rate
# = divide the time codes by a speed factor (to make it go faster/slower)
# = drop frames that are less than n msec apart (to accomodate slow players)
# - Convert to a different format
# - Magnify (scale) the image
# Usage function (keep this up-to-date if you change the program!)
def usage():
print 'Usage: Vcopy [options] [infile [outfile]]'
print
print 'Options:'
print
print '-t type : new image type (default unchanged)'
print
print '-M magnify : image magnification factor (default unchanged)'
print '-w width : output image width (default height*4/3 if -h used)'
print '-h height : output image height (default width*3/4 if -w used)'
print
print '-p pf : new x and y packfactor (default unchanged)'
print '-x xpf : new x packfactor (default 1 if -y used)'
print '-y ypf : new y packfactor (default 1 if -x used)'
print
print '-m delta : drop frames closer than delta msec (default 0)'
print '-r delta : regenerate input time base delta msec apart'
print '-s speed : speed change factor (default unchanged)'
print
print 'infile : input file (default film.video)'
print 'outfile : output file (default out.video)'
import sys import sys
import getopt sys.path.append('/ufs/guido/src/video')
from gl import *
from DEVICE import *
import VFile import VFile
import string import imgconv
import imageop import imageop
import getopt
import string
def report(time, iframe):
print 'Frame', iframe, ': t =', time
def usage(): # Global options
sys.stderr.write('usage: Vcopy [-t type] [-m treshold] [-a] infile outfile\n')
sys.stderr.write('-t Convert to other type\n') speed = 1.0
sys.stderr.write('-a Automatic\n') mindelta = 0
sys.stderr.write('-m Convert grey to mono with treshold\n') regen = None
sys.stderr.write('-d Convert grey to mono with dithering\n') newpf = None
sys.exit(2) newtype = None
magnify = None
def help(): newwidth = None
print 'Command summary:' newheight = None
print 'n get next image from input'
print 'w write current image to output'
# Function to turn a string into a float
atof_error = 'atof_error' # Exception if it fails
def atof(s):
try:
return float(eval(s))
except:
raise atof_error
# Main program -- mostly command line parsing
def main(): def main():
foreground() global speed, mindelta, regen, newpf, newtype, \
opts, args = getopt.getopt(sys.argv[1:], 't:am:d') magnify, newwidth, newheight
if len(args) <> 2:
# Parse command line
try:
opts, args = getopt.getopt(sys.argv[1:], \
'M:h:m:p:r:s:t:w:x:y:')
except getopt.error, msg:
sys.stdout = sys.stderr
print 'Error:', msg, '\n'
usage() usage()
[ifile, ofile] = args sys.exit(2)
print 'open film ', ifile
ifilm = VFile.VinFile().init(ifile) xpf = ypf = None
print 'open output ', ofile
ofilm = VFile.VoutFile().init(ofile)
ofilm.setinfo(ifilm.getinfo()) # Interpret options
try:
use_grabber = 0 for opt, arg in opts:
continuous = 0 if opt == '-M': magnify = atof(arg)
tomono = 0 if opt == '-h': height = string.atoi(arg)
tomonodither = 0 if opt == '-m': mindelta = string.atoi(arg)
for o, a in opts: if opt == '-p': xpf = ypf = string.atoi(arg)
if o == '-t': if opt == '-r': regen = string.atoi(arg)
ofilm.format = a if opt == '-s': speed = atof(arg)
use_grabber = 1 if opt == '-t': newtype = arg
if o == '-a': if opt == '-w': newwidth = string.atoi(arg)
continuous = 1 if opt == '-x': xpf = string.atoi(arg)
if o == '-m': if opt == '-y': ypf = string.atoi(arg)
if ifilm.format <> 'grey': except string.atoi_error:
print '-m only supported for greyscale' sys.stdout = sys.stderr
sys.exit(1) print 'Option', opt, 'requires integer argument'
tomono = 1 sys.exit(2)
treshold = string.atoi(a) except atof_error:
ofilm.format = 'mono' sys.stdout = sys.stderr
if o == '-d': print 'Option', opt, 'requires float argument'
if ifilm.format <> 'grey': sys.exit(2)
print '-m only supported for greyscale'
sys.exit(1) if xpf or ypf:
tomonodither = 1 if not xpf: xpf = 1
ofilm.format = 'mono' if not ypf: ypf = 1
newpf = (xpf, ypf)
ofilm.writeheader()
# if newwidth or newheight:
prefsize(ifilm.width, ifilm.height) if magnify:
w = winopen(ifile) sys.stdout = sys.stderr
qdevice(KEYBD) print 'Options -w or -h are incompatible with -M'
qdevice(ESCKEY) sys.exit(2)
qdevice(WINQUIT) if not newheight:
qdevice(WINSHUT) newheight = newwidth * 3 / 4
print 'qdevice calls done' elif not newwidth:
# newwidth = newheight * 4 / 3
help()
# # Check filename arguments
time, data, cdata = ifilm.getnextframe() if len(args) < 1:
ifilm.showframe(data, cdata) args.append('film.video')
iframe = 1 if len(args) < 2:
report(time, iframe) args.append('out.video')
# if len(args) > 2:
usage()
sys.exit(2)
if args[0] == args[1]:
sys.stderr.write('Input file can\'t be output file\n')
sys.exit(2)
# Do the right thing
sts = process(args[0], args[1])
# Exit
sys.exit(sts)
# Copy one file to another
def process(infilename, outfilename):
global newwidth, newheight
try:
vin = VFile.BasicVinFile().init(infilename)
except IOError, msg:
sys.stderr.write(infilename + ': I/O error: ' + `msg` + '\n')
return 1
except VFile.Error, msg:
sys.stderr.write(msg + '\n')
return 1
except EOFError:
sys.stderr.write(infilename + ': EOF in video file\n')
return 1
try:
vout = VFile.BasicVoutFile().init(outfilename)
except IOError, msg:
sys.stderr.write(outfilename + ': I/O error: ' + `msg` + '\n')
return 1
vout.setinfo(vin.getinfo())
scale = 0
flip = 0
if newtype:
vout.setformat(newtype)
try:
convert = imgconv.getconverter(vin.format, vout.format)
except imgconv.error, msg:
sys.stderr.write(str(msg) + '\n')
return 1
if newpf:
vout.setpf(newpf)
scale = 1
if newwidth and newheight:
scale = 1
if vin.upside_down <> vout.upside_down or \
vin.mirror_image <> vout.mirror_image:
flip = 1
inwidth, inheight = vin.getsize()
inwidth = inwidth / vin.xpf
inheight = inheight / vin.ypf
if magnify:
newwidth = int(vout.width * magnify)
newheight = int(vout.height * magnify)
scale = 1
if scale:
vout.setsize(newwidth, newheight)
else:
newwidth, newheight = vout.getsize()
if vin.packfactor <> vout.packfactor:
scale = 1
if scale or flip:
if vout.bpp not in (8, 32):
sys.stderr.write('Can\'t scale or flip this type\n')
return 1
newwidth = newwidth / vout.xpf
newheight = newheight / vout.ypf
vout.writeheader()
told = 0
nin = 0
nout = 0
tin = 0
tout = 0
while 1: while 1:
if continuous: try:
dev = KEYBD tin, data, cdata = vin.getnextframe()
else: except EOFError:
dev, val = qread()
if dev in (ESCKEY, WINQUIT, WINSHUT):
break break
if dev == REDRAW: nin = nin + 1
reshapeviewport() if regen:
elif dev == KEYBD: tout = nin * regen
if continuous:
c = '0'
else:
c = chr(val)
#XXX Debug
if c == 'R':
c3i(255,0,0)
clear()
if c == 'G':
c3i(0,255,0)
clear()
if c == 'B':
c3i(0,0,255)
clear()
if c == 'w' or continuous:
if use_grabber:
data, cdata = ofilm.grabframe()
if tomono:
data = imageop.grey2mono(data, \
ifilm.width, ifilm.height, \
treshold)
if tomonodither:
data = imageop.dither2mono(data, \
ifilm.width, ifilm.height)
ofilm.writeframe(time, data, cdata)
print 'Frame', iframe, 'written.'
if c == 'n' or continuous:
try:
time,data,cdata = ifilm.getnextframe()
ifilm.showframe(data, cdata)
iframe = iframe+1
report(time, iframe)
except EOFError:
print 'EOF'
if continuous:
break
ringbell()
elif dev == INPUTCHANGE:
pass
else: else:
print '(dev, val) =', (dev, val) tout = tin
ofilm.close() tout = int(tout / speed)
if tout - told < mindelta:
continue
told = tout
if newtype:
data = convert(data, inwidth, inheight)
if newwidth and newheight:
data = imageop.scale(data, vout.bpp/8, \
inwidth, inheight, newwidth, newheight)
if vin.upside_down <> vout.upside_down or \
vin.mirror_image <> vout.mirror_image:
x0, y0 = 0, 0
x1, y1 = newwidth-1, neheight-1
if vin.upside_down <> vout.upside_down:
y1, y0 = y0, y1
if vin.mirror_image <> vout.mirror_image:
x1, x0 = x0, x1
data = imageop.crop(data, vout.bpp/8, \
newwidth, newheight, x0, y0, x1, y1)
vout.writeframe(tout, data, cdata)
nout = nout + 1
vout.close()
vin.close()
# Don't forget to call the main program
main() try:
main()
except KeyboardInterrupt:
print '[Interrupt]'
...@@ -22,7 +22,7 @@ def rgb2jpeg(img, x, y): ...@@ -22,7 +22,7 @@ def rgb2jpeg(img, x, y):
def jpeggrey2grey(img, width, height): def jpeggrey2grey(img, width, height):
import jpeg import jpeg
data, width, height, bytesperpixel = jpeg.decompress(img) data, width, height, bytesperpixel = jpeg.decompress(img)
if bytesperpixel <> 1: raise RuntimeError, 'not grayscale jpeg' if bytesperpixel <> 1: raise RuntimeError, 'not greyscale jpeg'
return data return data
def jpeg2rgb(img, width, height): def jpeg2rgb(img, width, height):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment