Texture meta-data
On upload, j2c textures will have all CME segments in the main header removed to be replaced by a single CME segment. That segment will be serialized as an unordered key-value url query string with the following keys and associated values.
- a
- The agent_id of the resident that uploaded the texture.
- z
- The utc time the texture was uploaded in YYYYmmddHHMMSS format.
- w
- The original width of the image as reported by the viewer. This value must be interpretable as an integer greater than 0 and less than 1,000,000.
- h
- The original height of the image as reported by the viewer. This value must be interpretable as an integer greater than 0 and less than 1,000,000.
- c
- The average color of the image as calculated during verification. The value will be RRGGBBAA in hex with 00 being none and ff representing full saturation of that component. If the image only has three components, then alpha is set to ff. If there are more than four components, the extra components are skipped.
example
The actual encoded comment string will look something like:
a=abbee3b5-fbe0-4cfa-8d15-323d6800448e&h=480&w=640&z=20081118204138&c=26961aff
tool support
The j2c_comments.py
script understands j2c codestream CME markers and will parse for them in the main header. To use it, pass a list of j2c codestream file names on the command line. The script will emit the file name parsed and report on comments found for each. If the example above is in test.texture, the session would look something like:
$ ./j2c_comments.py test.texture test.texture: { 'a':['abbee3b5-fbe0-4cfa-8d15-323d6800448e'], 'h':['480'], 'w':['640'], 'z':['20081118204138'], 'c':['26961aff']}
The tool will emit error messages if the codestream could not be understood, or if the comment is not latin1. Comments which do not conform to the query string format will be treated as a plain, raw latin1 string.
j2c_comments.py
#!/usr/bin/env python
"""
Extract out all of the j2c CME markers
"""
import cgi
import optparse
import struct
class NotJ2C(Exception):
pass
def parse_cme(filename):
j2c = file(filename, 'rb')
comments = None
while True:
marker_bytes = j2c.read(2)
marker, marker_type = struct.unpack('BB', marker_bytes)
if marker != 0xff:
raise NotJ2C
if marker_type == 0x90:
# we found the SOT, so we're done.
return comments
elif marker_type == 0x4f:
# this is SOC, which does not have a lsoc element
continue
size_bytes = j2c.read(2)
size = struct.unpack('!H', size_bytes)[0] - 2
if marker_type == 0x64:
# found a CME. read it, and interpret it.
rcme = j2c.read(2)
rcme = struct.unpack('!H', rcme)[0]
if rcme == 0:
new_comment = '<binary>'
elif rcme == 1:
raw_comment = j2c.read(size - 2)
new_comment = cgi.parse_qs(raw_comment)
if len(new_comment) == 0:
new_comment = raw_comment
else:
new_comment = '<unknown>'
if comments is None:
comments = new_comment
elif type(comments) == 'list':
comments.append(new_comment)
else:
comments = [comments, new_comments]
continue
j2c.seek(size, 1)
continue
def main():
parser = optparse.OptionParser(
usage="usage: %prog [options] file_list",
description="Read through file list and extract the CME field")
options, args = parser.parse_args()
if len(args) < 1:
parser.error("Please specify one or more files to assign.")
for filename in args:
try:
comments = parse_cme(filename)
print filename + ': ' + str(comments)
except NotJ2C:
print filename + ': Unable to parse file'
if __name__ == "__main__":
main()