📄 vfile.py
字号:
# Classes to read and write CMIF video files.# (For a description of the CMIF video format, see cmif-file.ms.)# Layers of functionality:## VideoParams: maintain essential parameters of a video file# Displayer: display a frame in a window (with some extra parameters)# BasicVinFile: read a CMIF video file# BasicVoutFile: write a CMIF video file# VinFile: BasicVinFile + Displayer# VoutFile: BasicVoutFile + Displayer## XXX Future extension:# BasicVinoutFile: supports overwriting of individual frames# Imported modulesimport systry: import gl import GL import GET no_gl = 0except ImportError: no_gl = 1import colorsysimport imageop# Exception raised for various occasionsError = 'VFile.Error' # file format errorsCallError = 'VFile.CallError' # bad callAssertError = 'VFile.AssertError' # internal malfunction# Max nr. of colormap entries to useMAXMAP = 4096 - 256# Parametrizations of colormap handling based on color system.# (These functions are used via eval with a constructed argument!)def conv_grey(l, x, y): return colorsys.yiq_to_rgb(l, 0, 0)def conv_grey4(l, x, y): return colorsys.yiq_to_rgb(l*17, 0, 0)def conv_mono(l, x, y): return colorsys.yiq_to_rgb(l*255, 0, 0)def conv_yiq(y, i, q): return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5)def conv_hls(l, h, s): return colorsys.hls_to_rgb(h, l, s)def conv_hsv(v, h, s): return colorsys.hsv_to_rgb(h, s, v)def conv_rgb(r, g, b): raise Error, 'Attempt to make RGB colormap'def conv_rgb8(rgb, d1, d2): rgb = int(rgb*255.0) r = (rgb >> 5) & 0x07 g = (rgb ) & 0x07 b = (rgb >> 3) & 0x03 return (r/7.0, g/7.0, b/3.0)def conv_jpeg(r, g, b): raise Error, 'Attempt to make RGB colormap (jpeg)'conv_jpeggrey = conv_greyconv_grey2 = conv_grey# Choose one of the above based upon a color system namedef choose_conversion(format): try: return eval('conv_' + format) except: raise Error, 'Unknown color system: ' + `format`# Inverses of the abovedef inv_grey(r, g, b): y, i, q = colorsys.rgb_to_yiq(r, g, b) return y, 0, 0def inv_yiq(r, g, b): y, i, q = colorsys.rgb_to_yiq(r, g, b) return y, i/1.2 + 0.5, q + 0.5def inv_hls(r, g, b): h, l, s = colorsys.rgb_to_hls(r, g, b) return l, h, sdef inv_hsv(r, g, b): h, s, v = colorsys.rgb_to_hsv(r, g, b) return v, h, sdef inv_rgb(r, g, b): raise Error, 'Attempt to invert RGB colormap'def inv_rgb8(r, g, b): r = int(r*7.0) g = int(g*7.0) b = int(b*7.0) rgb = ((r&7) << 5) | ((b&3) << 3) | (g&7) return rgb / 255.0, 0, 0def inv_jpeg(r, g, b): raise Error, 'Attempt to invert RGB colormap (jpeg)'inv_jpeggrey = inv_grey# Choose one of the above based upon a color system namedef choose_inverse(format): try: return eval('inv_' + format) except: raise Error, 'Unknown color system: ' + `format`# Predicate to see whether this is an entry level (non-XS) Indigo.# If so we can lrectwrite 8-bit wide pixels into a window in RGB modedef is_entry_indigo(): # XXX hack, hack. We should call gl.gversion() but that doesn't # exist in earlier Python versions. Therefore we check the number # of bitplanes *and* the size of the monitor. xmax = gl.getgdesc(GL.GD_XPMAX) if xmax <> 1024: return 0 ymax = gl.getgdesc(GL.GD_YPMAX) if ymax != 768: return 0 r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED) g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN) b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE) return (r, g, b) == (3, 3, 2)# Predicate to see whether this machine supports pixmode(PM_SIZE) with# values 1 or 4.## XXX Temporarily disabled, since it is unclear which machines support# XXX which pixelsizes.## 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 is faster than using PM_SIZE=4)def support_packed_pixels(): return 0 # To be architecture-dependent# Tables listing bits per pixel for some formatsbitsperpixel = { \ 'rgb': 32, \ 'rgb8': 8, \ 'grey': 8, \ 'grey4': 4, \ 'grey2': 2, \ 'mono': 1, \ 'compress': 32, \}bppafterdecomp = {'jpeg': 32, 'jpeggrey': 8}# Base class to manage video format parametersclass VideoParams: # Initialize an instance. # Set all parameters to something decent # (except width and height are set to zero) def __init__(self): # Essential parameters self.frozen = 0 # if set, can't change parameters self.format = 'grey' # color system used # Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey, # mono, grey2, grey4 self.width = 0 # width of frame self.height = 0 # height of frame self.packfactor = 1, 1 # expansion using rectzoom # Colormap info self.c0bits = 8 # bits in first color dimension self.c1bits = 0 # bits in second color dimension self.c2bits = 0 # bits in third color dimension self.offset = 0 # colormap index offset (XXX ???) self.chrompack = 0 # set if separate chrominance data self.setderived() self.decompressor = None # 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 colormap info def setcmapinfo(self): stuff = 0, 0, 0, 0, 0 if self.format in ('rgb8', 'grey'): stuff = 8, 0, 0, 0, 0 if self.format == 'grey4': stuff = 4, 0, 0, 0, 0 if self.format == 'grey2': stuff = 2, 0, 0, 0, 0 if self.format == 'mono': stuff = 1, 0, 0, 0, 0 self.c0bits, self.c1bits, self.c2bits, \ self.offset, self.chrompack = stuff # Set the frame width and height (e.g. from gl.getsize()) 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.setderived() # Retrieve the frame width and height (e.g. for gl.prefsize()) def getsize(self): return (self.width, self.height) # Set the format def setformat(self, format): if self.frozen: raise CallError self.format = format self.setderived() self.setcmapinfo() # Get the format def getformat(self): return self.format # Set the packfactor def setpf(self, pf): if self.frozen: raise CallError if type(pf) == type(1): 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): if self.frozen: raise CallError self.setformat(values[0]) self.setpf(values[3]) self.setsize(values[1], values[2]) (self.c0bits, self.c1bits, self.c2bits, \ self.offset, self.chrompack) = values[4:9] if self.format == 'compress' and len(values) > 9: self.compressheader = values[9] self.setderived() # Retrieve all parameters in a format suitable for a subsequent # call to setinfo() def getinfo(self): return (self.format, self.width, self.height, self.packfactor,\ self.c0bits, self.c1bits, self.c2bits, self.offset, \ self.chrompack) def getcompressheader(self): return self.compressheader def setcompressheader(self, ch): self.compressheader = ch # Write the relevant bits to stdout def printinfo(self): print 'Format: ', self.format print 'Size: ', self.width, 'x', self.height print 'Pack: ', self.packfactor, '; chrom:', self.chrompack print 'Bpp: ', self.bpp print 'Bits: ', self.c0bits, self.c1bits, self.c2bits print 'Offset: ', self.offset # Calculate data size, if possible # (Not counting frame header or cdata size) def calcframesize(self): if not self.bpp: raise CallError size = self.width/self.xpf * self.height/self.ypf size = (size * self.bpp + 7) / 8 return size # Decompress a possibly compressed frame. This method is here # since you sometimes want to use it on a VFile instance and sometimes # on a Displayer instance. # # XXXX This should also handle jpeg. Actually, the whole mechanism # should be much more of 'ihave/iwant' style, also allowing you to # read, say, greyscale images from a color movie. def decompress(self, data): if self.format <> 'compress': return data if not self.decompressor: import cl scheme = cl.QueryScheme(self.compressheader) self.decompressor = cl.OpenDecompressor(scheme) headersize = self.decompressor.ReadHeader(self.compressheader) width = self.decompressor.GetParam(cl.IMAGE_WIDTH) height = self.decompressor.GetParam(cl.IMAGE_HEIGHT) params = [cl.ORIGINAL_FORMAT, cl.RGBX, \ cl.ORIENTATION, cl.BOTTOM_UP, \ cl.FRAME_BUFFER_SIZE, width*height*cl.BytesPerPixel(cl.RGBX)] self.decompressor.SetParams(params) data = self.decompressor.Decompress(1, data) return data# Class to display video frames in a window.# It is the caller's responsibility to ensure that the correct window# is current when using showframe(), initcolormap(), clear() and clearto()class Displayer(VideoParams): # Initialize an instance. # This does not need a current window def __init__(self): if no_gl: raise RuntimeError, \ 'no gl module available, so cannot display' VideoParams.__init__(self) # User-settable parameters self.magnify = 1.0 # frame magnification factor self.xorigin = 0 # x frame offset self.yorigin = 0 # y frame offset (from bottom) self.quiet = 0 # if set, don't print messages self.fallback = 1 # allow fallback to grey # Internal flags self.colormapinited = 0 # must initialize window self.skipchrom = 0 # don't skip chrominance data self.color0 = None # magic, used by clearto() self.fixcolor0 = 0 # don't need to fix color0 self.mustunpack = (not support_packed_pixels()) # setinfo() must reset some internal flags def setinfo(self, values): VideoParams.setinfo(self, values) self.colormapinited = 0 self.skipchrom = 0 self.color0 = None self.fixcolor0 = 0 # Show one frame, initializing the window if necessary def showframe(self, data, chromdata): self.showpartframe(data, chromdata, \ (0,0,self.width,self.height)) def showpartframe(self, data, chromdata, (x,y,w,h)): pmsize = self.bpp xpf, ypf = self.xpf, self.ypf if self.upside_down: gl.pixmode(GL.PM_TTOB, 1) if self.mirror_image: gl.pixmode(GL.PM_RTOL, 1) if self.format in ('jpeg', 'jpeggrey'): import jpeg data, width, height, bytes = jpeg.decompress(data) pmsize = bytes*8 elif self.format == 'compress': data = self.decompress(data) pmsize = 32 elif self.format in ('mono', 'grey4'): if self.mustunpack: if self.format == 'mono': data = imageop.mono2grey(data, \ w/xpf, h/ypf, 0x20, 0xdf) elif self.format == 'grey4': data = imageop.grey42grey(data, \ w/xpf, h/ypf) pmsize = 8 elif self.format == 'grey2': data = imageop.grey22grey(data, w/xpf, h/ypf) pmsize = 8 if not self.colormapinited: self.initcolormap() if self.fixcolor0: gl.mapcolor(self.color0) self.fixcolor0 = 0 xfactor = yfactor = self.magnify xfactor = xfactor * xpf yfactor = yfactor * ypf if chromdata and not self.skipchrom: cp = self.chrompack cx = int(x*xfactor*cp) + self.xorigin cy = int(y*yfactor*cp) + self.yorigin cw = (w+cp-1)/cp ch = (h+cp-1)/cp gl.rectzoom(xfactor*cp, yfactor*cp) gl.pixmode(GL.PM_SIZE, 16) gl.writemask(self.mask - ((1 << self.c0bits) - 1)) gl.lrectwrite(cx, cy, cx + cw - 1, cy + ch - 1, \ chromdata) # if pmsize < 32: gl.writemask((1 << self.c0bits) - 1) gl.pixmode(GL.PM_SIZE, pmsize) w = w/xpf h = h/ypf x = x/xpf y = y/ypf gl.rectzoom(xfactor, yfactor) x = int(x*xfactor)+self.xorigin y = int(y*yfactor)+self.yorigin gl.lrectwrite(x, y, x + w - 1, y + h - 1, data) gl.gflush() # Initialize the window: set RGB or colormap mode as required, # fill in the colormap, and clear the window def initcolormap(self): self.colormapinited = 1 self.color0 = None self.fixcolor0 = 0 if self.format in ('rgb', 'jpeg', 'compress'): self.set_rgbmode() gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.clear() return # This only works on an Entry-level Indigo from IRIX 4.0.5 if self.format == 'rgb8' and is_entry_indigo() and \ gl.gversion() == 'GL4DLG-4.0.': # Note trailing '.'! self.set_rgbmode() gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.clear() gl.pixmode(GL.PM_SIZE, 8) return self.set_cmode() self.skipchrom = 0 if self.offset == 0: self.mask = 0x7ff else: self.mask = 0xfff if not self.quiet: sys.stderr.write('Initializing color map...') self._initcmap() gl.clear() if not self.quiet: sys.stderr.write(' Done.\n') # Set the window in RGB mode (may be overridden for Glx window) def set_rgbmode(self): gl.RGBmode() gl.gconfig() # Set the window in colormap mode (may be overridden for Glx window) def set_cmode(self): gl.cmode() gl.gconfig() # Clear the window to a default color def clear(self): if not self.colormapinited: raise CallError if gl.getdisplaymode() in (GET.DMRGB, GET.DMRGBDOUBLE): gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.clear() return gl.writemask(0xffffffff) gl.clear() # Clear the window to a given RGB color. # This may steal the first color index used; the next call to # showframe() will restore the intended mapping for that index def clearto(self, r, g, b): if not self.colormapinited: raise CallError if gl.getdisplaymode() in (GET.DMRGB, GET.DMRGBDOUBLE): gl.RGBcolor(r, g, b) gl.clear() return index = self.color0[0] self.fixcolor0 = 1 gl.mapcolor(index, r, g, b) gl.writemask(0xffffffff) gl.clear() gl.gflush() # Do the hard work for initializing the colormap (internal). # This also sets the current color to the first color index # used -- the caller should never change this since it is used # by clear() and clearto() def _initcmap(self): map = [] if self.format in ('mono', 'grey4') and self.mustunpack: convcolor = conv_grey else: convcolor = choose_conversion(self.format) maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE) if maxbits > 11: maxbits = 11 c0bits = self.c0bits c1bits = self.c1bits c2bits = self.c2bits if c0bits+c1bits+c2bits > maxbits: if self.fallback and c0bits < maxbits: # Cannot display frames in this mode, use grey self.skipchrom = 1 c1bits = c2bits = 0 convcolor = choose_conversion('grey') else: raise Error, 'Sorry, '+`maxbits`+ \ ' bits max on this machine' maxc0 = 1 << c0bits maxc1 = 1 << c1bits maxc2 = 1 << c2bits if self.offset == 0 and maxbits == 11: offset = 2048 else: offset = self.offset if maxbits <> 11: offset = offset & ((1<<maxbits)-1) self.color0 = None self.fixcolor0 = 0 for c0 in range(maxc0): c0v = c0/float(maxc0-1) for c1 in range(maxc1): if maxc1 == 1: c1v = 0 else: c1v = c1/float(maxc1-1) for c2 in range(maxc2): if maxc2 == 1: c2v = 0 else: c2v = c2/float(maxc2-1) index = offset + c0 + (c1<<c0bits) + \ (c2 << (c0bits+c1bits)) if index < MAXMAP: rv, gv, bv = \ convcolor(c0v, c1v, c2v) r, g, b = int(rv*255.0), \ int(gv*255.0), \ int(bv*255.0) map.append((index, r, g, b)) if self.color0 == None: self.color0 = \ index, r, g, b self.install_colormap(map)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -