| #!/usr/bin/env python |
| # $URL$ |
| # $Rev$ |
| # PDS Image to PNG |
| |
| import re |
| import struct |
| |
| import png |
| |
| class FormatError(Exception): |
| pass |
| |
| def pdskey(s, k): |
| """Lookup key `k` in string `s`. Returns value (as a string), or |
| raises exception if not found. |
| """ |
| |
| assert re.match(r' *\^?[:\w]+$', k) |
| safere = '^' + re.escape(k) +r' *= *(\w+)' |
| m = re.search(safere, s, re.MULTILINE) |
| if not m: |
| raise FormatError("Can't find %s." % k) |
| return m.group(1) |
| |
| def img(inp): |
| """Open the PDS IMG file `inp` and return (*pixels*, *info*). |
| *pixels* is an iterator over the rows, *info* is the information |
| dictionary. |
| """ |
| |
| err = __import__('sys').stderr |
| |
| consumed = 1024 |
| |
| s = inp.read(consumed) |
| record_type = pdskey(s, 'RECORD_TYPE') |
| if record_type != 'FIXED_LENGTH': |
| raise FormatError( |
| "Can only deal with FIXED_LENGTH record type (found %s)" % |
| record_type) |
| record_bytes = int(pdskey(s,'RECORD_BYTES')) |
| file_records = int(pdskey(s, 'FILE_RECORDS')) |
| label_records = int(pdskey(s, 'LABEL_RECORDS')) |
| remaining = label_records * record_bytes - consumed |
| s += inp.read(remaining) |
| consumed += remaining |
| |
| image_pointer = int(pdskey(s, '^IMAGE')) |
| # "^IMAGE" locates a record. Records are numbered starting from 1. |
| image_index = image_pointer - 1 |
| image_offset = image_index * record_bytes |
| gap = image_offset - consumed |
| assert gap >= 0 |
| if gap: |
| inp.read(gap) |
| # This assumes there is only one OBJECT in the file, and it is the |
| # IMAGE. |
| height = int(pdskey(s, ' LINES')) |
| width = int(pdskey(s, ' LINE_SAMPLES')) |
| sample_type = pdskey(s, ' SAMPLE_TYPE') |
| sample_bits = int(pdskey(s, ' SAMPLE_BITS')) |
| # For Messenger MDIS, SAMPLE_BITS is reported as 16, but only values |
| # from 0 ot 4095 are used. |
| bitdepth = 12 |
| if sample_type == 'MSB_UNSIGNED_INTEGER': |
| fmt = '>H' |
| else: |
| raise 'Unknown sample type: %s.' % sample_type |
| sample_bytes = (1,2)[bitdepth > 8] |
| row_bytes = sample_bytes * width |
| fmt = fmt[:1] + str(width) + fmt[1:] |
| def rowiter(): |
| for y in range(height): |
| yield struct.unpack(fmt, inp.read(row_bytes)) |
| info = dict(greyscale=True, alpha=False, bitdepth=bitdepth, |
| size=(width,height), gamma=1.0) |
| return rowiter(), info |
| |
| |
| def main(argv=None): |
| import sys |
| |
| if argv is None: |
| argv = sys.argv |
| argv = argv[1:] |
| arg = argv |
| if len(arg) >= 1: |
| f = open(arg[0], 'rb') |
| else: |
| f = sys.stdin |
| pixels,info = img(f) |
| w = png.Writer(**info) |
| w.write(sys.stdout, pixels) |
| |
| if __name__ == '__main__': |
| main() |
| |
| |