VFile: added new formats 'jpeg' and 'jpeggrey'. Decompression is done

using module 'jpeg' by the Displayer class.  (Unfortunately it's too
slow for real time.)  Print file size in printinfo() method.

Vinfo: added -t option (terse -- one line per file) and usage message.

Vtime: use BasicV{in,out}File classes -- the minimum needed.

Vmkjpeg, Vunjpeg: new utilities for jpeg (de)compression.
This commit is contained in:
Guido van Rossum 1992-09-29 13:40:47 +00:00
parent 3165fe6a56
commit 9e3f335bea
6 changed files with 299 additions and 33 deletions

View File

@ -24,12 +24,13 @@ editor, not in this directory but in /ufs/guido/mm/.)
When we got our own Indigo entry-level video board (in June 1992) and When we got our own Indigo entry-level video board (in June 1992) and
a version of the Irix video library that supported capturing PAL a version of the Irix video library that supported capturing PAL
format (in August 1992), Sjoerd added an interface to the video format (in August 1992), Sjoerd added an interface to the video
library to Python (sv) and Guido wrote Vrec.py (based upon a library to Python (sv) and Guido wrote Vrec.py (based upon a still
still frame grabber by Sjoerd, in turn based upon SGI demo code in C) frame grabber by Sjoerd, in turn based upon SGI demo code in C) to
to record a movie using it. Vrec was soon followed by modernized record a movie using it. Vrec was soon followed by modernized
versions of the other programs (Vinfo, Vplay, Vtime) and an versions of the other programs (Vinfo, Vplay, Vtime) and an
interactive editor (Vedit). Finally, VFile was rewritten for more interactive editor (Vedit). Finally, VFile was rewritten for more
modularity, functionality and robustness. modularity, functionality and robustness, and various other tools were
added as needed.
Guido van Rossum Guido van Rossum
Jack Jansen Jack Jansen
@ -39,7 +40,8 @@ modularity, functionality and robustness.
Overview of files Overview of files
----------------- -----------------
cmif-film.ms description of the CMIF video file format cmif-film.ms description of the CMIF video file format (a little
out of date)
These are programs with a command line interface: These are programs with a command line interface:
@ -55,6 +57,10 @@ Vtime.py (unrelated to vtime!!!) Copy a video file,
manipulating the time codes (e.g. faster/slower, or manipulating the time codes (e.g. faster/slower, or
regenerate time codes, or drop frames too close apart) regenerate time codes, or drop frames too close apart)
Vmkjpeg.py compress an rgb or grey video file to jpeg[grey] format
Vunjpeg.py expand a jpeg[grey] video file to rgb or grey format
Vedit.py interactive video editing program Vedit.py interactive video editing program
Vsend.py unicast or multicast live video as UDP packets Vsend.py unicast or multicast live video as UDP packets

View File

@ -69,6 +69,11 @@ def conv_rgb8(rgb, d1, d2):
b = (rgb >> 3) & 0x03 b = (rgb >> 3) & 0x03
return (r/7.0, g/7.0, b/3.0) 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_grey
# Choose one of the above based upon a color system name # Choose one of the above based upon a color system name
@ -107,6 +112,11 @@ def inv_rgb8(r, g, b):
rgb = ((r&7) << 5) | ((b&3) << 3) | (g&7) rgb = ((r&7) << 5) | ((b&3) << 3) | (g&7)
return rgb / 255.0, 0, 0 return rgb / 255.0, 0, 0
def 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 name # Choose one of the above based upon a color system name
@ -175,6 +185,13 @@ def grab_hls(w, h, pf):
def grab_hsv(w, h, pf): def grab_hsv(w, h, pf):
raise Error, 'Sorry, grabbing hsv not implemented' 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'
# Choose one of the above based upon a color system name # Choose one of the above based upon a color system name
@ -196,7 +213,7 @@ class VideoParams:
def init(self): def init(self):
# Essential parameters # Essential parameters
self.format = 'grey' # color system used self.format = 'grey' # color system used
# Choose from: 'rgb', 'rgb8', 'hsv', 'yiq', 'hls' # Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey
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 # expansion using rectzoom
@ -286,12 +303,22 @@ 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
if self.format in ('jpeg', 'jpeggrey'):
import jpeg
data, width, height, bytes = jpeg.decompress(data)
if self.format == 'jpeg':
b = 4
else:
b = 1
width, height = width*pf, height*pf
if (width, height, bytes) <> (w, h, b):
raise Error, 'jpeg data has wrong size'
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
pf = self.packfactor
factor = self.magnify factor = self.magnify
if pf: factor = factor * pf if pf: factor = factor * pf
if chromdata and not self.skipchrom: if chromdata and not self.skipchrom:
@ -326,7 +353,7 @@ class Displayer(VideoParams):
self.colormapinited = 1 self.colormapinited = 1
self.color0 = None self.color0 = None
self.fixcolor0 = 0 self.fixcolor0 = 0
if self.format == 'rgb': if self.format in ('rgb', 'jpeg'):
gl.RGBmode() gl.RGBmode()
gl.gconfig() gl.gconfig()
gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.RGBcolor(200, 200, 200) # XXX rather light grey
@ -509,11 +536,11 @@ def readfileheader(fp, filename):
format, rest = eval(line[:-1]) format, rest = eval(line[:-1])
except: except:
raise Error, filename + ': Bad 3.0 color info' raise Error, filename + ': Bad 3.0 color info'
if format == 'rgb': if format in ('rgb', 'jpeg'):
c0bits = c1bits = c2bits = 0 c0bits = c1bits = c2bits = 0
chrompack = 0 chrompack = 0
offset = 0 offset = 0
elif format == 'grey': elif format in ('grey', 'jpeggrey'):
c0bits = rest c0bits = rest
c1bits = c2bits = 0 c1bits = c2bits = 0
chrompack = 0 chrompack = 0
@ -606,17 +633,17 @@ def writefileheader(fp, values):
# #
# Write color encoding info # Write color encoding info
# #
if format == 'rgb': if format in ('rgb', 'jpeg'):
data = ('rgb', 0) data = (format, 0)
elif format == 'grey': elif format in ('grey', 'jpeggrey'):
data = ('grey', c0bits) data = (format, c0bits)
else: else:
data = (format, (c0bits, c1bits, c2bits, chrompack, offset)) data = (format, (c0bits, c1bits, c2bits, chrompack, offset))
fp.write(`data`+'\n') fp.write(`data`+'\n')
# #
# Write frame geometry info # Write frame geometry info
# #
if format == 'rgb': if format in ('rgb', 'jpeg'):
packfactor = 0 packfactor = 0
elif packfactor == 0: elif packfactor == 0:
packfactor = 1 packfactor = 1
@ -699,6 +726,7 @@ class BasicVinFile(VideoParams):
def printinfo(self): def printinfo(self):
print 'File: ', self.filename print 'File: ', self.filename
print 'Size: ', getfilesize(self.filename)
print 'Version: ', self.version print 'Version: ', self.version
VideoParams.printinfo(self) VideoParams.printinfo(self)
@ -765,6 +793,17 @@ class BasicVinFile(VideoParams):
self.framecount = self.framecount + 1 self.framecount = self.framecount + 1
# Subroutine to return a file's size in bytes
def getfilesize(filename):
import os, stat
try:
st = os.stat(filename)
return st[stat.ST_SIZE]
except os.error:
return 0
# Derived class implementing random access and index cached in the file # Derived class implementing random access and index cached in the file
class RandomVinFile(BasicVinFile): class RandomVinFile(BasicVinFile):

View File

@ -20,6 +20,7 @@ import sys
sys.path.append('/ufs/guido/src/video') sys.path.append('/ufs/guido/src/video')
import VFile import VFile
import getopt import getopt
import string
# Global options # Global options
@ -27,22 +28,34 @@ import getopt
short = 0 short = 0
quick = 0 quick = 0
delta = 0 delta = 0
terse = 0
maxwidth = 10
# Main program -- mostly command line parsing # Main program -- mostly command line parsing
def main(): def main():
global short, quick, delta global short, quick, delta, terse, maxwidth
opts, args = getopt.getopt(sys.argv[1:], 'dqs') try:
opts, args = getopt.getopt(sys.argv[1:], 'dqst')
except getopt.error, msg:
sys.stdout = sys.stderr
print msg
print 'usage: Vinfo [-d] [-q] [-s] [-t] [file] ...'
sys.exit(2)
for opt, arg in opts: for opt, arg in opts:
if opt == '-q': if opt == '-q':
quick = 1 quick = 1
elif opt == '-d': if opt == '-d':
delta = 1 delta = 1
elif opt == '-s': if opt == '-s':
short = 1 short = 1
if opt == '-t':
terse = short = 1
if not args: if not args:
args = ['film.video'] args = ['film.video']
for filename in args:
maxwidth = max(maxwidth, len(filename))
sts = 0 sts = 0
for filename in args: for filename in args:
if process(filename): if process(filename):
@ -65,17 +78,31 @@ def process(filename):
sys.stderr.write(filename + ': EOF in video file\n') sys.stderr.write(filename + ': EOF in video file\n')
return 1 return 1
vin.printinfo() if terse:
print string.ljust(filename, maxwidth),
kbytes = (VFile.getfilesize(filename) + 1023) / 1024
print string.rjust(`kbytes`, 5) + 'K',
print ' ', string.ljust(`vin.version`, 5),
print string.ljust(vin.format, 8),
print string.rjust(`vin.width`, 4),
print string.rjust(`vin.height`, 4),
sys.stdout.flush()
else:
vin.printinfo()
if quick: if quick:
if terse:
print
vin.close() vin.close()
return return 0
try: try:
vin.readcache() vin.readcache()
print '[Using cached index]' if not terse:
print '[Using cached index]'
except VFile.Error: except VFile.Error:
print '[Constructing index on the fly]' if not terse:
print '[Constructing index on the fly]'
if not short: if not short:
if delta: if delta:
@ -107,16 +134,21 @@ def process(filename):
if not short: print if not short: print
print 'Total', n, 'frames in', t*0.001, 'sec.', if terse:
if t: print '-- average', int(n*10000.0/t)*0.1, 'frames/sec', print string.rjust(`n`, 6),
print print string.rjust(`int(n*10000.0/t)*0.1`, 5)
print 'Total data', 0.1 * int(datasize / 102.4), 'Kbytes', else:
if t: print 'Total', n, 'frames in', t*0.001, 'sec.',
print '-- average', if t: print '-- average', int(n*10000.0/t)*0.1, 'frames/sec',
print 0.1 * int(datasize / 0.1024 / t), 'Kbytes/sec', print
print print 'Total data', 0.1 * int(datasize / 102.4), 'Kbytes',
if t:
print '-- average',
print 0.1 * int(datasize / 0.1024 / t), 'Kbytes/sec',
print
vin.close() vin.close()
return 0
# Don't forget to call the main program # Don't forget to call the main program

92
Demo/sgi/video/Vmkjpeg.py Executable file
View File

@ -0,0 +1,92 @@
#!/ufs/guido/bin/sgi/python
# Compress an rgb or grey video file to jpeg format
# Usage:
#
# Vmkjpeg [infile [outfile]]
# Options:
#
# infile : input file (default film.video)
# outfile : output file (default out.video)
import sys
import jpeg
sys.path.append('/ufs/guido/src/video')
import VFile
# Main program -- mostly command line parsing
def main():
args = sys.argv[1:]
if len(args) < 1:
args.append('film.video')
if len(args) < 2:
args.append('out.video')
if len(args) > 2:
sys.stderr.write('usage: Vmkjpeg [infile [outfile]]\n')
sys.exit(2)
sts = process(args[0], args[1])
sys.exit(sts)
# Copy one file to another
def process(infilename, outfilename):
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
info = vin.getinfo()
if info[0] == 'rgb':
width, height = vin.getsize()
bytes = 4
format = 'jpeg'
elif info[0] == 'grey':
width, height = vin.getsize()
pf = vin.packfactor
width, height = width / pf, height / pf
bytes = 1
format = 'jpeggrey'
else:
sys.stderr.write('Vmkjpeg: input not in rgb or grey format\n')
return 1
info = (format,) + info[1:]
vout.setinfo(info)
vout.writeheader()
n = 0
try:
while 1:
t, data, cdata = vin.getnextframe()
n = n + 1
sys.stderr.write('Frame ' + `n` + '...')
data = jpeg.compress(data, width, height, bytes)
vout.writeframe(t, data, None)
sys.stderr.write('\n')
except EOFError:
pass
return 0
# Don't forget to call the main program
main()

View File

@ -65,7 +65,7 @@ def main():
def process(infilename, outfilename): def process(infilename, outfilename):
try: try:
vin = VFile.VinFile().init(infilename) vin = VFile.BasicVinFile().init(infilename)
except IOError, msg: except IOError, msg:
sys.stderr.write(infilename + ': I/O error: ' + `msg` + '\n') sys.stderr.write(infilename + ': I/O error: ' + `msg` + '\n')
return 1 return 1
@ -77,7 +77,7 @@ def process(infilename, outfilename):
return 1 return 1
try: try:
vout = VFile.VoutFile().init(outfilename) vout = VFile.BasicVoutFile().init(outfilename)
except IOError, msg: except IOError, msg:
sys.stderr.write(outfilename + ': I/O error: ' + `msg` + '\n') sys.stderr.write(outfilename + ': I/O error: ' + `msg` + '\n')
return 1 return 1

97
Demo/sgi/video/Vunjpeg.py Executable file
View File

@ -0,0 +1,97 @@
#!/ufs/guido/bin/sgi/python
# Decompress a jpeg or jpeggrey video file to rgb format
# Usage:
#
# Vunjpeg [infile [outfile]]
# Options:
#
# infile : input file (default film.video)
# outfile : output file (default out.video)
import sys
import jpeg
sys.path.append('/ufs/guido/src/video')
import VFile
# Main program -- mostly command line parsing
def main():
args = sys.argv[1:]
if len(args) < 1:
args.append('film.video')
if len(args) < 2:
args.append('out.video')
if len(args) > 2:
sys.stderr.write('usage: Vunjpeg [infile [outfile]]\n')
sys.exit(2)
sts = process(args[0], args[1])
sys.exit(sts)
# Copy one file to another
def process(infilename, outfilename):
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
info = vin.getinfo()
if info[0] == 'jpeg':
format = 'rgb'
width, height = vin.getsize()
bytes = 4
elif info[0] == 'jpeggrey':
format = 'grey'
width, height = vin.getsize()
pf = vin.packfactor
width, height = width/pf, height/pf
bytes = 1
else:
sys.stderr.write('Vunjpeg: input not in jpeg[grey] format\n')
return 1
info = (format,) + info[1:]
vout.setinfo(info)
vout.writeheader()
sts = 0
n = 0
try:
while 1:
t, data, cdata = vin.getnextframe()
n = n + 1
sys.stderr.write('Frame ' + `n` + '...')
data, w, h, b = jpeg.decompress(data)
if (w, h, b) <> (width, height, bytes):
sys.stderr.write('jpeg data has wrong size\n')
sts = 1
else:
vout.writeframe(t, data, None)
sys.stderr.write('\n')
except EOFError:
pass
return sts
# Don't forget to call the main program
main()