updates
This commit is contained in:
355
Backend/venv/bin/pripamtopng
Executable file
355
Backend/venv/bin/pripamtopng
Executable file
@@ -0,0 +1,355 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# pripamtopng
|
||||
#
|
||||
# Python Raster Image PAM to PNG
|
||||
|
||||
import array
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import png
|
||||
|
||||
Description = """Convert NetPBM PAM/PNM format files to PNG."""
|
||||
|
||||
|
||||
def read_pam_header(infile):
|
||||
"""
|
||||
Read (the rest of a) PAM header.
|
||||
`infile` should be positioned immediately after the initial 'P7' line
|
||||
(at the beginning of the second line).
|
||||
Returns are as for `read_pnm_header`.
|
||||
"""
|
||||
|
||||
# Unlike PBM, PGM, and PPM, we can read the header a line at a time.
|
||||
header = dict()
|
||||
while True:
|
||||
line = infile.readline().strip()
|
||||
if line == b"ENDHDR":
|
||||
break
|
||||
if not line:
|
||||
raise EOFError("PAM ended prematurely")
|
||||
if line[0] == b"#":
|
||||
continue
|
||||
line = line.split(None, 1)
|
||||
key = line[0]
|
||||
if key not in header:
|
||||
header[key] = line[1]
|
||||
else:
|
||||
header[key] += b" " + line[1]
|
||||
|
||||
required = [b"WIDTH", b"HEIGHT", b"DEPTH", b"MAXVAL"]
|
||||
required_str = b", ".join(required).decode("ascii")
|
||||
result = []
|
||||
for token in required:
|
||||
if token not in header:
|
||||
raise png.Error("PAM file must specify " + required_str)
|
||||
try:
|
||||
x = int(header[token])
|
||||
except ValueError:
|
||||
raise png.Error(required_str + " must all be valid integers")
|
||||
if x <= 0:
|
||||
raise png.Error(required_str + " must all be positive integers")
|
||||
result.append(x)
|
||||
|
||||
return (b"P7",) + tuple(result)
|
||||
|
||||
|
||||
def read_pnm_header(infile):
|
||||
"""
|
||||
Read a PNM header, returning (format,width,height,depth,maxval).
|
||||
Also reads a PAM header (by using a helper function).
|
||||
`width` and `height` are in pixels.
|
||||
`depth` is the number of channels in the image;
|
||||
for PBM and PGM it is synthesized as 1, for PPM as 3;
|
||||
for PAM images it is read from the header.
|
||||
`maxval` is synthesized (as 1) for PBM images.
|
||||
"""
|
||||
|
||||
# Generally, see http://netpbm.sourceforge.net/doc/ppm.html
|
||||
# and http://netpbm.sourceforge.net/doc/pam.html
|
||||
|
||||
# Technically 'P7' must be followed by a newline,
|
||||
# so by using rstrip() we are being liberal in what we accept.
|
||||
# I think this is acceptable.
|
||||
magic = infile.read(3).rstrip()
|
||||
if magic == b"P7":
|
||||
# PAM header parsing is completely different.
|
||||
return read_pam_header(infile)
|
||||
|
||||
# Expected number of tokens in header (3 for P4, 4 for P6)
|
||||
expected = 4
|
||||
pbm = (b"P1", b"P4")
|
||||
if magic in pbm:
|
||||
expected = 3
|
||||
header = [magic]
|
||||
|
||||
# We must read the rest of the header byte by byte because
|
||||
# the final whitespace character may not be a newline.
|
||||
# Of course all PNM files in the wild use a newline at this point,
|
||||
# but we are strong and so we avoid
|
||||
# the temptation to use readline.
|
||||
bs = bytearray()
|
||||
backs = bytearray()
|
||||
|
||||
def next():
|
||||
if backs:
|
||||
c = bytes(backs[0:1])
|
||||
del backs[0]
|
||||
else:
|
||||
c = infile.read(1)
|
||||
if not c:
|
||||
raise png.Error("premature EOF reading PNM header")
|
||||
bs.extend(c)
|
||||
return c
|
||||
|
||||
def backup():
|
||||
"""Push last byte of token onto front of backs."""
|
||||
backs.insert(0, bs[-1])
|
||||
del bs[-1]
|
||||
|
||||
def ignore():
|
||||
del bs[:]
|
||||
|
||||
def tokens():
|
||||
ls = lexInit
|
||||
while True:
|
||||
token, ls = ls()
|
||||
if token:
|
||||
yield token
|
||||
|
||||
def lexInit():
|
||||
c = next()
|
||||
# Skip comments
|
||||
if b"#" <= c <= b"#":
|
||||
while c not in b"\n\r":
|
||||
c = next()
|
||||
ignore()
|
||||
return None, lexInit
|
||||
# Skip whitespace (that precedes a token)
|
||||
if c.isspace():
|
||||
ignore()
|
||||
return None, lexInit
|
||||
if not c.isdigit():
|
||||
raise png.Error("unexpected byte %r found in header" % c)
|
||||
return None, lexNumber
|
||||
|
||||
def lexNumber():
|
||||
# According to the specification it is legal to have comments
|
||||
# that appear in the middle of a token.
|
||||
# I've never seen it; and,
|
||||
# it's a bit awkward to code good lexers in Python (no goto).
|
||||
# So we break on such cases.
|
||||
c = next()
|
||||
while c.isdigit():
|
||||
c = next()
|
||||
backup()
|
||||
token = bs[:]
|
||||
ignore()
|
||||
return token, lexInit
|
||||
|
||||
for token in tokens():
|
||||
# All "tokens" are decimal integers, so convert them here.
|
||||
header.append(int(token))
|
||||
if len(header) == expected:
|
||||
break
|
||||
|
||||
final = next()
|
||||
if not final.isspace():
|
||||
raise png.Error("expected header to end with whitespace, not %r" % final)
|
||||
|
||||
if magic in pbm:
|
||||
# synthesize a MAXVAL
|
||||
header.append(1)
|
||||
depth = (1, 3)[magic == b"P6"]
|
||||
return header[0], header[1], header[2], depth, header[3]
|
||||
|
||||
|
||||
def convert_pnm_plain(w, infile, outfile):
|
||||
"""
|
||||
Convert a plain PNM file containing raw pixel data into
|
||||
a PNG file with the parameters set in the writer object.
|
||||
Works for plain PGM formats.
|
||||
"""
|
||||
|
||||
# See convert_pnm_binary for the corresponding function for
|
||||
# binary PNM formats.
|
||||
|
||||
rows = scan_rows_from_file_plain(infile, w.width, w.height, w.planes)
|
||||
w.write(outfile, rows)
|
||||
|
||||
|
||||
def scan_rows_from_file_plain(infile, width, height, planes):
|
||||
"""
|
||||
Generate a sequence of rows from the input file `infile`.
|
||||
The input file should be in a "Netpbm-like" plain format.
|
||||
The input file should be positioned at the beginning of the
|
||||
first value (that is, immediately after the header).
|
||||
The number of pixels to read is taken from
|
||||
the image dimensions (`width`, `height`, `planes`).
|
||||
|
||||
Each row is yielded as a single sequence of values.
|
||||
"""
|
||||
|
||||
# Values per row
|
||||
vpr = width * planes
|
||||
|
||||
values = []
|
||||
rows_output = 0
|
||||
|
||||
# The core problem is that input lines (text lines) may not
|
||||
# correspond with pixel rows. We use two nested loops.
|
||||
# The outer loop reads the input one text line at a time;
|
||||
# this will contain a whole number of values, which are
|
||||
# added to the `values` list.
|
||||
# The inner loop strips the first `vpr` values from the
|
||||
# list, until there aren't enough.
|
||||
# Note we can't tell how many iterations the inner loop will
|
||||
# run for, it could be 0 (if not enough values were read to
|
||||
# make a whole pixel row) or many (if the entire image were
|
||||
# on one input line), or somewhere in between.
|
||||
# In PNM there is in general no requirement to have
|
||||
# correspondence between text lines and pixel rows.
|
||||
|
||||
for inp in infile:
|
||||
values.extend(map(int, inp.split()))
|
||||
while len(values) >= vpr:
|
||||
yield values[:vpr]
|
||||
del values[:vpr]
|
||||
rows_output += 1
|
||||
if rows_output >= height:
|
||||
# Diagnostic here if there are spare values?
|
||||
return
|
||||
# Diagnostic here for early EOF?
|
||||
|
||||
|
||||
def convert_pnm_binary(w, infile, outfile):
|
||||
"""
|
||||
Convert a PNM file containing raw pixel data into
|
||||
a PNG file with the parameters set in the writer object.
|
||||
Works for (binary) PGM, PPM, and PAM formats.
|
||||
"""
|
||||
|
||||
rows = scan_rows_from_file(infile, w.width, w.height, w.planes, w.bitdepth)
|
||||
w.write(outfile, rows)
|
||||
|
||||
|
||||
def scan_rows_from_file(infile, width, height, planes, bitdepth):
|
||||
"""
|
||||
Generate a sequence of rows from the input file `infile`.
|
||||
The input file should be in a "Netpbm-like" binary format.
|
||||
The input file should be positioned at the beginning of the first pixel.
|
||||
The number of pixels to read is taken from
|
||||
the image dimensions (`width`, `height`, `planes`);
|
||||
the number of bytes per value is implied by `bitdepth`.
|
||||
Each row is yielded as a single sequence of values.
|
||||
"""
|
||||
|
||||
# Values per row
|
||||
vpr = width * planes
|
||||
# Bytes per row
|
||||
bpr = vpr
|
||||
if bitdepth > 8:
|
||||
assert bitdepth == 16
|
||||
bpr *= 2
|
||||
fmt = ">%dH" % vpr
|
||||
|
||||
def line():
|
||||
return array.array("H", struct.unpack(fmt, infile.read(bpr)))
|
||||
|
||||
else:
|
||||
|
||||
def line():
|
||||
return array.array("B", infile.read(bpr))
|
||||
|
||||
for y in range(height):
|
||||
yield line()
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
"""
|
||||
Create a parser and parse the command line arguments.
|
||||
"""
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(description=Description)
|
||||
version = "%(prog)s " + png.__version__
|
||||
parser.add_argument("--version", action="version", version=version)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--compression",
|
||||
type=int,
|
||||
metavar="level",
|
||||
help="zlib compression level (0-9)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"input",
|
||||
nargs="?",
|
||||
default="-",
|
||||
type=png.cli_open,
|
||||
metavar="PAM/PNM",
|
||||
help="input PAM/PNM file to convert",
|
||||
)
|
||||
args = parser.parse_args(args)
|
||||
return args
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
args = parse_args(argv[1:])
|
||||
|
||||
# Prepare input and output files
|
||||
infile = args.input
|
||||
|
||||
# Call after parsing, so that --version and --help work.
|
||||
outfile = png.binary_stdout()
|
||||
|
||||
# Encode PNM to PNG
|
||||
format, width, height, depth, maxval = read_pnm_header(infile)
|
||||
|
||||
ok_formats = (b"P2", b"P5", b"P6", b"P7")
|
||||
if format not in ok_formats:
|
||||
raise NotImplementedError("file format %s not supported" % format)
|
||||
|
||||
# The NetPBM depth (number of channels) completely
|
||||
# determines the PNG format.
|
||||
# Observe:
|
||||
# - L, LA, RGB, RGBA are the 4 modes supported by PNG;
|
||||
# - they correspond to 1, 2, 3, 4 channels respectively.
|
||||
# We use the number of channels in the source image to
|
||||
# determine which one we have.
|
||||
# We ignore the NetPBM image type and the PAM TUPLTYPE.
|
||||
greyscale = depth <= 2
|
||||
pamalpha = depth in (2, 4)
|
||||
supported = [2 ** x - 1 for x in range(1, 17)]
|
||||
try:
|
||||
mi = supported.index(maxval)
|
||||
except ValueError:
|
||||
raise NotImplementedError(
|
||||
"input maxval (%s) not in supported list %s" % (maxval, str(supported))
|
||||
)
|
||||
bitdepth = mi + 1
|
||||
writer = png.Writer(
|
||||
width,
|
||||
height,
|
||||
greyscale=greyscale,
|
||||
bitdepth=bitdepth,
|
||||
alpha=pamalpha,
|
||||
compression=args.compression,
|
||||
)
|
||||
|
||||
plain = format in (b"P1", b"P2", b"P3")
|
||||
if plain:
|
||||
convert_pnm_plain(writer, infile, outfile)
|
||||
else:
|
||||
convert_pnm_binary(writer, infile, outfile)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except png.Error as e:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(99)
|
||||
Reference in New Issue
Block a user