updates
This commit is contained in:
266
Backend/venv/bin/prichunkpng
Executable file
266
Backend/venv/bin/prichunkpng
Executable file
@@ -0,0 +1,266 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
# prichunkpng
|
||||
# Chunk editing tool.
|
||||
|
||||
"""
|
||||
Make a new PNG by adding, delete, or replacing particular chunks.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
|
||||
# https://docs.python.org/2.7/library/io.html
|
||||
import io
|
||||
import re
|
||||
import string
|
||||
import struct
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
# Local module.
|
||||
import png
|
||||
|
||||
|
||||
Chunk = collections.namedtuple("Chunk", "type content")
|
||||
|
||||
|
||||
class ArgumentError(Exception):
|
||||
"""A user problem with the command arguments."""
|
||||
|
||||
|
||||
def process(out, args):
|
||||
"""Process the PNG file args.input to the output, chunk by chunk.
|
||||
Chunks can be inserted, removed, replaced, or sometimes edited.
|
||||
Chunks are specified by their 4 byte Chunk Type;
|
||||
see https://www.w3.org/TR/2003/REC-PNG-20031110/#5Chunk-layout .
|
||||
The chunks in args.delete will be removed from the stream.
|
||||
The chunks in args.chunk will be inserted into the stream
|
||||
with their contents taken from the named files.
|
||||
|
||||
Other options on the args object will create particular
|
||||
ancillary chunks.
|
||||
|
||||
.gamma -> gAMA chunk
|
||||
.sigbit -> sBIT chunk
|
||||
|
||||
Chunk types need not be official PNG chunks at all.
|
||||
Non-standard chunks can be created.
|
||||
"""
|
||||
|
||||
# Convert options to chunks in the args.chunk list
|
||||
if args.gamma:
|
||||
v = int(round(1e5 * args.gamma))
|
||||
bs = io.BytesIO(struct.pack(">I", v))
|
||||
args.chunk.insert(0, Chunk(b"gAMA", bs))
|
||||
if args.sigbit:
|
||||
v = struct.pack("%dB" % len(args.sigbit), *args.sigbit)
|
||||
bs = io.BytesIO(v)
|
||||
args.chunk.insert(0, Chunk(b"sBIT", bs))
|
||||
if args.iccprofile:
|
||||
# http://www.w3.org/TR/PNG/#11iCCP
|
||||
v = b"a color profile\x00\x00" + zlib.compress(args.iccprofile.read())
|
||||
bs = io.BytesIO(v)
|
||||
args.chunk.insert(0, Chunk(b"iCCP", bs))
|
||||
if args.transparent:
|
||||
# https://www.w3.org/TR/2003/REC-PNG-20031110/#11tRNS
|
||||
v = struct.pack(">%dH" % len(args.transparent), *args.transparent)
|
||||
bs = io.BytesIO(v)
|
||||
args.chunk.insert(0, Chunk(b"tRNS", bs))
|
||||
if args.background:
|
||||
# https://www.w3.org/TR/2003/REC-PNG-20031110/#11bKGD
|
||||
v = struct.pack(">%dH" % len(args.background), *args.background)
|
||||
bs = io.BytesIO(v)
|
||||
args.chunk.insert(0, Chunk(b"bKGD", bs))
|
||||
if args.physical:
|
||||
# https://www.w3.org/TR/PNG/#11pHYs
|
||||
numbers = re.findall(r"(\d+\.?\d*)", args.physical)
|
||||
if len(numbers) not in {1, 2}:
|
||||
raise ArgumentError("One or two numbers are required for --physical")
|
||||
xppu = float(numbers[0])
|
||||
if len(numbers) == 1:
|
||||
yppu = xppu
|
||||
else:
|
||||
yppu = float(numbers[1])
|
||||
|
||||
unit_spec = 0
|
||||
if args.physical.endswith("dpi"):
|
||||
# Convert from DPI to Pixels Per Metre
|
||||
# 1 inch is 0.0254 metres
|
||||
l = 0.0254
|
||||
xppu /= l
|
||||
yppu /= l
|
||||
unit_spec = 1
|
||||
elif args.physical.endswith("ppm"):
|
||||
unit_spec = 1
|
||||
|
||||
v = struct.pack("!LLB", round(xppu), round(yppu), unit_spec)
|
||||
bs = io.BytesIO(v)
|
||||
args.chunk.insert(0, Chunk(b"pHYs", bs))
|
||||
|
||||
# Create:
|
||||
# - a set of chunks to delete
|
||||
# - a dict of chunks to replace
|
||||
# - a list of chunk to add
|
||||
|
||||
delete = set(args.delete)
|
||||
# The set of chunks to replace are those where the specification says
|
||||
# that there should be at most one of them.
|
||||
replacing = set([b"gAMA", b"pHYs", b"sBIT", b"PLTE", b"tRNS", b"sPLT", b"IHDR"])
|
||||
replace = dict()
|
||||
add = []
|
||||
|
||||
for chunk in args.chunk:
|
||||
if chunk.type in replacing:
|
||||
replace[chunk.type] = chunk
|
||||
else:
|
||||
add.append(chunk)
|
||||
|
||||
input = png.Reader(file=args.input)
|
||||
|
||||
return png.write_chunks(out, edit_chunks(input.chunks(), delete, replace, add))
|
||||
|
||||
|
||||
def edit_chunks(chunks, delete, replace, add):
|
||||
"""
|
||||
Iterate over chunks, yielding edited chunks.
|
||||
Subtle: the new chunks have to have their contents .read().
|
||||
"""
|
||||
for type, v in chunks:
|
||||
if type in delete:
|
||||
continue
|
||||
if type in replace:
|
||||
yield type, replace[type].content.read()
|
||||
del replace[type]
|
||||
continue
|
||||
|
||||
if b"IDAT" <= type <= b"IDAT" and replace:
|
||||
# If there are any chunks on the replace list by
|
||||
# the time we reach IDAT, add then all now.
|
||||
# put them all on the add list.
|
||||
for chunk in replace.values():
|
||||
yield chunk.type, chunk.content.read()
|
||||
replace = dict()
|
||||
|
||||
if b"IDAT" <= type <= b"IDAT" and add:
|
||||
# We reached IDAT; add all remaining chunks now.
|
||||
for chunk in add:
|
||||
yield chunk.type, chunk.content.read()
|
||||
add = []
|
||||
|
||||
yield type, v
|
||||
|
||||
|
||||
def chunk_name(s):
|
||||
"""
|
||||
Type check a chunk name option value.
|
||||
"""
|
||||
|
||||
# See https://www.w3.org/TR/2003/REC-PNG-20031110/#table51
|
||||
valid = len(s) == 4 and set(s) <= set(string.ascii_letters)
|
||||
if not valid:
|
||||
raise ValueError("Chunk name must be 4 ASCII letters")
|
||||
return s.encode("ascii")
|
||||
|
||||
|
||||
def comma_list(s):
|
||||
"""
|
||||
Convert s, a command separated list of whole numbers,
|
||||
into a sequence of int.
|
||||
"""
|
||||
|
||||
return tuple(int(v) for v in s.split(","))
|
||||
|
||||
|
||||
def hex_color(s):
|
||||
"""
|
||||
Type check and convert a hex color.
|
||||
"""
|
||||
|
||||
if s.startswith("#"):
|
||||
s = s[1:]
|
||||
valid = len(s) in [1, 2, 3, 4, 6, 12] and set(s) <= set(string.hexdigits)
|
||||
if not valid:
|
||||
raise ValueError("colour must be 1,2,3,4,6, or 12 hex-digits")
|
||||
|
||||
# For the 4-bit RGB, expand to 8-bit, by repeating digits.
|
||||
if len(s) == 3:
|
||||
s = "".join(c + c for c in s)
|
||||
|
||||
if len(s) in [1, 2, 4]:
|
||||
# Single grey value.
|
||||
return (int(s, 16),)
|
||||
|
||||
if len(s) in [6, 12]:
|
||||
w = len(s) // 3
|
||||
return tuple(int(s[i : i + w], 16) for i in range(0, len(s), w))
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
argv = argv[1:]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--gamma", type=float, help="Gamma value for gAMA chunk")
|
||||
parser.add_argument(
|
||||
"--physical",
|
||||
type=str,
|
||||
metavar="x[,y][dpi|ppm]",
|
||||
help="specify intended pixel size or aspect ratio",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sigbit",
|
||||
type=comma_list,
|
||||
metavar="D[,D[,D[,D]]]",
|
||||
help="Number of significant bits in each channel",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iccprofile",
|
||||
metavar="file.iccp",
|
||||
type=argparse.FileType("rb"),
|
||||
help="add an ICC Profile from a file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--transparent",
|
||||
type=hex_color,
|
||||
metavar="#RRGGBB",
|
||||
help="Specify the colour that is transparent (tRNS chunk)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--background",
|
||||
type=hex_color,
|
||||
metavar="#RRGGBB",
|
||||
help="background colour for bKGD chunk",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--delete",
|
||||
action="append",
|
||||
default=[],
|
||||
type=chunk_name,
|
||||
help="delete the chunk",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--chunk",
|
||||
action="append",
|
||||
nargs=2,
|
||||
default=[],
|
||||
type=str,
|
||||
help="insert chunk, taking contents from file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
# Reprocess the chunk arguments, converting each pair into a Chunk.
|
||||
args.chunk = [
|
||||
Chunk(chunk_name(type), open(path, "rb")) for type, path in args.chunk
|
||||
]
|
||||
|
||||
return process(png.binary_stdout(), args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
81
Backend/venv/bin/pricolpng
Executable file
81
Backend/venv/bin/pricolpng
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# http://www.python.org/doc/2.4.4/lib/module-itertools.html
|
||||
import itertools
|
||||
import sys
|
||||
|
||||
import png
|
||||
|
||||
Description = """Join PNG images in a column top-to-bottom."""
|
||||
|
||||
|
||||
class FormatError(Exception):
|
||||
"""
|
||||
Some problem with the image format.
|
||||
"""
|
||||
|
||||
|
||||
def join_col(out, l):
|
||||
"""
|
||||
Join the list of images.
|
||||
All input images must be same width and
|
||||
have the same number of channels.
|
||||
They are joined top-to-bottom.
|
||||
`out` is the (open file) destination for the output image.
|
||||
`l` should be a list of open files (the input image files).
|
||||
"""
|
||||
|
||||
image = 0
|
||||
stream = 0
|
||||
|
||||
# When the first image is read, this will be the reference width,
|
||||
# which must be the same for all images.
|
||||
width = None
|
||||
# Total height (accumulated as images are read).
|
||||
height = 0
|
||||
# Accumulated rows.
|
||||
rows = []
|
||||
|
||||
for f in l:
|
||||
stream += 1
|
||||
while True:
|
||||
im = png.Reader(file=f)
|
||||
try:
|
||||
im.preamble()
|
||||
except EOFError:
|
||||
break
|
||||
image += 1
|
||||
|
||||
if not width:
|
||||
width = im.width
|
||||
elif width != im.width:
|
||||
raise FormatError('Image %d in stream %d has width %d; does not match %d.' %
|
||||
(image, stream, im.width, width))
|
||||
|
||||
height += im.height
|
||||
# Various bugs here because different numbers of channels and depths go wrong.
|
||||
w, h, p, info = im.asDirect()
|
||||
rows.extend(p)
|
||||
|
||||
# Alarmingly re-use the last info object.
|
||||
tinfo = dict(info)
|
||||
del tinfo['size']
|
||||
w = png.Writer(width, height, **tinfo)
|
||||
|
||||
w.write(out, rows)
|
||||
|
||||
|
||||
def main(argv):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description=Description)
|
||||
parser.add_argument(
|
||||
"input", nargs="*", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return join_col(png.binary_stdout(), args.input)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
254
Backend/venv/bin/priditherpng
Executable file
254
Backend/venv/bin/priditherpng
Executable file
@@ -0,0 +1,254 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# pipdither
|
||||
# Error Diffusing image dithering.
|
||||
# Now with serpentine scanning.
|
||||
|
||||
# See http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
|
||||
|
||||
# http://www.python.org/doc/2.4.4/lib/module-bisect.html
|
||||
from bisect import bisect_left
|
||||
|
||||
|
||||
import png
|
||||
|
||||
|
||||
def dither(
|
||||
out,
|
||||
input,
|
||||
bitdepth=1,
|
||||
linear=False,
|
||||
defaultgamma=1.0,
|
||||
targetgamma=None,
|
||||
cutoff=0.5, # see :cutoff:default
|
||||
):
|
||||
"""Dither the input PNG `inp` into an image with a smaller bit depth
|
||||
and write the result image onto `out`. `bitdepth` specifies the bit
|
||||
depth of the new image.
|
||||
|
||||
Normally the source image gamma is honoured (the image is
|
||||
converted into a linear light space before being dithered), but
|
||||
if the `linear` argument is true then the image is treated as
|
||||
being linear already: no gamma conversion is done (this is
|
||||
quicker, and if you don't care much about accuracy, it won't
|
||||
matter much).
|
||||
|
||||
Images with no gamma indication (no ``gAMA`` chunk) are normally
|
||||
treated as linear (gamma = 1.0), but often it can be better
|
||||
to assume a different gamma value: For example continuous tone
|
||||
photographs intended for presentation on the web often carry
|
||||
an implicit assumption of being encoded with a gamma of about
|
||||
0.45 (because that's what you get if you just "blat the pixels"
|
||||
onto a PC framebuffer), so ``defaultgamma=0.45`` might be a
|
||||
good idea. `defaultgamma` does not override a gamma value
|
||||
specified in the file itself: It is only used when the file
|
||||
does not specify a gamma.
|
||||
|
||||
If you (pointlessly) specify both `linear` and `defaultgamma`,
|
||||
`linear` wins.
|
||||
|
||||
The gamma of the output image is, by default, the same as the input
|
||||
image. The `targetgamma` argument can be used to specify a
|
||||
different gamma for the output image. This effectively recodes the
|
||||
image to a different gamma, dithering as we go. The gamma specified
|
||||
is the exponent used to encode the output file (and appears in the
|
||||
output PNG's ``gAMA`` chunk); it is usually less than 1.
|
||||
|
||||
"""
|
||||
|
||||
# Encoding is what happened when the PNG was made (and also what
|
||||
# happens when we output the PNG). Decoding is what we do to the
|
||||
# source PNG in order to process it.
|
||||
|
||||
# The dithering algorithm is not completely general; it
|
||||
# can only do bit depth reduction, not arbitrary palette changes.
|
||||
import operator
|
||||
|
||||
maxval = 2 ** bitdepth - 1
|
||||
r = png.Reader(file=input)
|
||||
|
||||
_, _, pixels, info = r.asDirect()
|
||||
planes = info["planes"]
|
||||
# :todo: make an Exception
|
||||
assert planes == 1
|
||||
width = info["size"][0]
|
||||
sourcemaxval = 2 ** info["bitdepth"] - 1
|
||||
|
||||
if linear:
|
||||
gamma = 1
|
||||
else:
|
||||
gamma = info.get("gamma") or defaultgamma
|
||||
|
||||
# Calculate an effective gamma for input and output;
|
||||
# then build tables using those.
|
||||
|
||||
# `gamma` (whether it was obtained from the input file or an
|
||||
# assumed value) is the encoding gamma.
|
||||
# We need the decoding gamma, which is the reciprocal.
|
||||
decode = 1.0 / gamma
|
||||
|
||||
# `targetdecode` is the assumed gamma that is going to be used
|
||||
# to decoding the target PNG.
|
||||
# Note that even though we will _encode_ the target PNG we
|
||||
# still need the decoding gamma, because
|
||||
# the table we use maps from PNG pixel value to linear light level.
|
||||
if targetgamma is None:
|
||||
targetdecode = decode
|
||||
else:
|
||||
targetdecode = 1.0 / targetgamma
|
||||
|
||||
incode = build_decode_table(sourcemaxval, decode)
|
||||
|
||||
# For encoding, we still build a decode table, because we
|
||||
# use it inverted (searching with bisect).
|
||||
outcode = build_decode_table(maxval, targetdecode)
|
||||
|
||||
# The table used for choosing output codes. These values represent
|
||||
# the cutoff points between two adjacent output codes.
|
||||
# The cutoff parameter can be varied between 0 and 1 to
|
||||
# preferentially choose lighter (when cutoff > 0.5) or
|
||||
# darker (when cutoff < 0.5) values.
|
||||
# :cutoff:default: The default for this used to be 0.75, but
|
||||
# testing by drj on 2021-07-30 showed that this produces
|
||||
# banding when dithering left-to-right gradients;
|
||||
# test with:
|
||||
# priforgepng grl | priditherpng | kitty icat
|
||||
choosecode = list(zip(outcode[1:], outcode))
|
||||
p = cutoff
|
||||
choosecode = [x[0] * p + x[1] * (1.0 - p) for x in choosecode]
|
||||
|
||||
rows = repeat_header(pixels)
|
||||
dithered_rows = run_dither(incode, choosecode, outcode, width, rows)
|
||||
dithered_rows = remove_header(dithered_rows)
|
||||
|
||||
info["bitdepth"] = bitdepth
|
||||
info["gamma"] = 1.0 / targetdecode
|
||||
w = png.Writer(**info)
|
||||
w.write(out, dithered_rows)
|
||||
|
||||
|
||||
def build_decode_table(maxval, gamma):
|
||||
"""Build a lookup table for decoding;
|
||||
table converts from pixel values to linear space.
|
||||
"""
|
||||
|
||||
assert maxval == int(maxval)
|
||||
assert maxval > 0
|
||||
|
||||
f = 1.0 / maxval
|
||||
table = [f * v for v in range(maxval + 1)]
|
||||
if gamma != 1.0:
|
||||
table = [v ** gamma for v in table]
|
||||
return table
|
||||
|
||||
|
||||
def run_dither(incode, choosecode, outcode, width, rows):
|
||||
"""
|
||||
Run an serpentine dither.
|
||||
Using the incode and choosecode tables.
|
||||
"""
|
||||
|
||||
# Errors diffused downwards (into next row)
|
||||
ed = [0.0] * width
|
||||
flipped = False
|
||||
for row in rows:
|
||||
# Convert to linear...
|
||||
row = [incode[v] for v in row]
|
||||
# Add errors...
|
||||
row = [e + v for e, v in zip(ed, row)]
|
||||
|
||||
if flipped:
|
||||
row = row[::-1]
|
||||
targetrow = [0] * width
|
||||
|
||||
for i, v in enumerate(row):
|
||||
# `it` will be the index of the chosen target colour;
|
||||
it = bisect_left(choosecode, v)
|
||||
targetrow[i] = it
|
||||
t = outcode[it]
|
||||
# err is the error that needs distributing.
|
||||
err = v - t
|
||||
|
||||
# Sierra "Filter Lite" distributes * 2
|
||||
# as per this diagram. 1 1
|
||||
ef = err * 0.5
|
||||
# :todo: consider making rows one wider at each end and
|
||||
# removing "if"s
|
||||
if i + 1 < width:
|
||||
row[i + 1] += ef
|
||||
ef *= 0.5
|
||||
ed[i] = ef
|
||||
if i:
|
||||
ed[i - 1] += ef
|
||||
|
||||
if flipped:
|
||||
ed = ed[::-1]
|
||||
targetrow = targetrow[::-1]
|
||||
yield targetrow
|
||||
flipped = not flipped
|
||||
|
||||
|
||||
WARMUP_ROWS = 32
|
||||
|
||||
|
||||
def repeat_header(rows):
|
||||
"""Repeat the first row, to "warm up" the error register."""
|
||||
for row in rows:
|
||||
yield row
|
||||
for _ in range(WARMUP_ROWS):
|
||||
yield row
|
||||
break
|
||||
yield from rows
|
||||
|
||||
|
||||
def remove_header(rows):
|
||||
"""Remove the same number of rows that repeat_header added."""
|
||||
|
||||
for _ in range(WARMUP_ROWS):
|
||||
next(rows)
|
||||
yield from rows
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import sys
|
||||
|
||||
# https://docs.python.org/3.5/library/argparse.html
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
progname, *args = argv
|
||||
|
||||
parser.add_argument("--bitdepth", type=int, default=1, help="bitdepth of output")
|
||||
parser.add_argument(
|
||||
"--cutoff",
|
||||
type=float,
|
||||
default=0.5,
|
||||
help="cutoff to select adjacent output values",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--defaultgamma",
|
||||
type=float,
|
||||
default=1.0,
|
||||
help="gamma value to use when no gamma in input",
|
||||
)
|
||||
parser.add_argument("--linear", action="store_true", help="force linear input")
|
||||
parser.add_argument(
|
||||
"--targetgamma",
|
||||
type=float,
|
||||
help="gamma to use in output (target), defaults to input gamma",
|
||||
)
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
ns = parser.parse_args(args)
|
||||
|
||||
return dither(png.binary_stdout(), **vars(ns))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
275
Backend/venv/bin/priforgepng
Executable file
275
Backend/venv/bin/priforgepng
Executable file
@@ -0,0 +1,275 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
# priforgepng
|
||||
|
||||
"""Forge PNG image from raw computation."""
|
||||
|
||||
from array import array
|
||||
from fractions import Fraction
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
|
||||
import png
|
||||
|
||||
|
||||
def gen_glr(x):
|
||||
"""Gradient Left to Right"""
|
||||
return x
|
||||
|
||||
|
||||
def gen_grl(x):
|
||||
"""Gradient Right to Left"""
|
||||
return 1 - x
|
||||
|
||||
|
||||
def gen_gtb(x, y):
|
||||
"""Gradient Top to Bottom"""
|
||||
return y
|
||||
|
||||
|
||||
def gen_gbt(x, y):
|
||||
"""Gradient Bottom to Top"""
|
||||
return 1.0 - y
|
||||
|
||||
|
||||
def gen_rtl(x, y):
|
||||
"""Radial gradient, centred at Top-Left"""
|
||||
return max(1 - (float(x) ** 2 + float(y) ** 2) ** 0.5, 0.0)
|
||||
|
||||
|
||||
def gen_rctr(x, y):
|
||||
"""Radial gradient, centred at Centre"""
|
||||
return gen_rtl(float(x) - 0.5, float(y) - 0.5)
|
||||
|
||||
|
||||
def gen_rtr(x, y):
|
||||
"""Radial gradient, centred at Top-Right"""
|
||||
return gen_rtl(1.0 - float(x), y)
|
||||
|
||||
|
||||
def gen_rbl(x, y):
|
||||
"""Radial gradient, centred at Bottom-Left"""
|
||||
return gen_rtl(x, 1.0 - float(y))
|
||||
|
||||
|
||||
def gen_rbr(x, y):
|
||||
"""Radial gradient, centred at Bottom-Right"""
|
||||
return gen_rtl(1.0 - float(x), 1.0 - float(y))
|
||||
|
||||
|
||||
def stripe(x, n):
|
||||
return int(x * n) & 1
|
||||
|
||||
|
||||
def gen_vs2(x):
|
||||
"""2 Vertical Stripes"""
|
||||
return stripe(x, 2)
|
||||
|
||||
|
||||
def gen_vs4(x):
|
||||
"""4 Vertical Stripes"""
|
||||
return stripe(x, 4)
|
||||
|
||||
|
||||
def gen_vs10(x):
|
||||
"""10 Vertical Stripes"""
|
||||
return stripe(x, 10)
|
||||
|
||||
|
||||
def gen_hs2(x, y):
|
||||
"""2 Horizontal Stripes"""
|
||||
return stripe(float(y), 2)
|
||||
|
||||
|
||||
def gen_hs4(x, y):
|
||||
"""4 Horizontal Stripes"""
|
||||
return stripe(float(y), 4)
|
||||
|
||||
|
||||
def gen_hs10(x, y):
|
||||
"""10 Horizontal Stripes"""
|
||||
return stripe(float(y), 10)
|
||||
|
||||
|
||||
def gen_slr(x, y):
|
||||
"""10 diagonal stripes, rising from Left to Right"""
|
||||
return stripe(x + y, 10)
|
||||
|
||||
|
||||
def gen_srl(x, y):
|
||||
"""10 diagonal stripes, rising from Right to Left"""
|
||||
return stripe(1 + x - y, 10)
|
||||
|
||||
|
||||
def checker(x, y, n):
|
||||
return stripe(x, n) ^ stripe(y, n)
|
||||
|
||||
|
||||
def gen_ck8(x, y):
|
||||
"""8 by 8 checkerboard"""
|
||||
return checker(x, y, 8)
|
||||
|
||||
|
||||
def gen_ck15(x, y):
|
||||
"""15 by 15 checkerboard"""
|
||||
return checker(x, y, 15)
|
||||
|
||||
|
||||
def gen_zero(x):
|
||||
"""All zero (black)"""
|
||||
return 0
|
||||
|
||||
|
||||
def gen_one(x):
|
||||
"""All one (white)"""
|
||||
return 1
|
||||
|
||||
|
||||
def yield_fun_rows(size, bitdepth, pattern):
|
||||
"""
|
||||
Create a single channel (monochrome) test pattern.
|
||||
Yield each row in turn.
|
||||
"""
|
||||
|
||||
width, height = size
|
||||
|
||||
maxval = 2 ** bitdepth - 1
|
||||
if maxval > 255:
|
||||
typecode = "H"
|
||||
else:
|
||||
typecode = "B"
|
||||
pfun = pattern_function(pattern)
|
||||
|
||||
# The coordinates are an integer + 0.5,
|
||||
# effectively sampling each pixel at its centre.
|
||||
# This is morally better, and produces all 256 sample values
|
||||
# in a 256-pixel wide gradient.
|
||||
|
||||
# We make a list of x coordinates here and re-use it,
|
||||
# because Fraction instances are slow to allocate.
|
||||
xs = [Fraction(x, 2 * width) for x in range(1, 2 * width, 2)]
|
||||
|
||||
# The general case is a function in x and y,
|
||||
# but if the function only takes an x argument,
|
||||
# it's handled in a special case that is a lot faster.
|
||||
if n_args(pfun) == 2:
|
||||
for y in range(height):
|
||||
a = array(typecode)
|
||||
fy = Fraction(Fraction(y + 0.5), height)
|
||||
for fx in xs:
|
||||
a.append(int(round(maxval * pfun(fx, fy))))
|
||||
yield a
|
||||
return
|
||||
|
||||
# For functions in x only, it's a _lot_ faster
|
||||
# to generate a single row and repeatedly yield it
|
||||
a = array(typecode)
|
||||
for fx in xs:
|
||||
a.append(int(round(maxval * pfun(x=fx))))
|
||||
for y in range(height):
|
||||
yield a
|
||||
return
|
||||
|
||||
|
||||
def generate(args):
|
||||
"""
|
||||
Create a PNG test image and write the file to stdout.
|
||||
|
||||
`args` should be an argparse Namespace instance or similar.
|
||||
"""
|
||||
|
||||
size = args.size
|
||||
bitdepth = args.depth
|
||||
|
||||
out = png.binary_stdout()
|
||||
|
||||
for pattern in args.pattern:
|
||||
rows = yield_fun_rows(size, bitdepth, pattern)
|
||||
writer = png.Writer(
|
||||
size[0], size[1], bitdepth=bitdepth, greyscale=True, alpha=False
|
||||
)
|
||||
writer.write(out, rows)
|
||||
|
||||
|
||||
def n_args(fun):
|
||||
"""Number of arguments in fun's argument list."""
|
||||
return fun.__code__.co_argcount
|
||||
|
||||
|
||||
def pattern_function(pattern):
|
||||
"""From `pattern`, a string,
|
||||
return the function for that pattern.
|
||||
"""
|
||||
|
||||
lpat = pattern.lower()
|
||||
for name, fun in globals().items():
|
||||
parts = name.split("_")
|
||||
if parts[0] != "gen":
|
||||
continue
|
||||
if parts[1] == lpat:
|
||||
return fun
|
||||
|
||||
|
||||
def patterns():
|
||||
"""
|
||||
List the patterns.
|
||||
"""
|
||||
|
||||
for name, fun in globals().items():
|
||||
parts = name.split("_")
|
||||
if parts[0] == "gen":
|
||||
yield parts[1], fun.__doc__
|
||||
|
||||
|
||||
def dimensions(s):
|
||||
"""
|
||||
Typecheck the --size option, which should be
|
||||
one or two comma separated numbers.
|
||||
Example: "64,40".
|
||||
"""
|
||||
|
||||
tupl = re.findall(r"\d+", s)
|
||||
if len(tupl) not in (1, 2):
|
||||
raise ValueError("%r should be width or width,height" % s)
|
||||
if len(tupl) == 1:
|
||||
tupl *= 2
|
||||
assert len(tupl) == 2
|
||||
return list(map(int, tupl))
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
parser = argparse.ArgumentParser(description="Forge greyscale PNG patterns")
|
||||
|
||||
parser.add_argument(
|
||||
"-l", "--list", action="store_true", help="print list of patterns and exit"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--depth", default=8, type=int, metavar="N", help="N bits per pixel"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--size",
|
||||
default=[256, 256],
|
||||
type=dimensions,
|
||||
metavar="w[,h]",
|
||||
help="width and height of the image in pixels",
|
||||
)
|
||||
parser.add_argument("pattern", nargs="*", help="name of pattern")
|
||||
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
if args.list:
|
||||
for name, doc in sorted(patterns()):
|
||||
print(name, doc, sep="\t")
|
||||
return
|
||||
|
||||
if not args.pattern:
|
||||
parser.error("--list or pattern is required")
|
||||
return generate(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
Backend/venv/bin/prigreypng
Executable file
72
Backend/venv/bin/prigreypng
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# prigreypng
|
||||
|
||||
# Convert image to grey (L, or LA), but only if that involves no colour change.
|
||||
|
||||
import argparse
|
||||
import array
|
||||
|
||||
|
||||
import png
|
||||
|
||||
|
||||
def as_grey(out, inp):
|
||||
"""
|
||||
Convert image to greyscale, but only when no colour change.
|
||||
This works by using the input G channel (green) as
|
||||
the output L channel (luminance) and
|
||||
checking that every pixel is grey as we go.
|
||||
A non-grey pixel will raise an error.
|
||||
"""
|
||||
|
||||
r = png.Reader(file=inp)
|
||||
_, _, rows, info = r.asDirect()
|
||||
if info["greyscale"]:
|
||||
w = png.Writer(**info)
|
||||
return w.write(out, rows)
|
||||
|
||||
planes = info["planes"]
|
||||
targetplanes = planes - 2
|
||||
alpha = info["alpha"]
|
||||
width, height = info["size"]
|
||||
typecode = "BH"[info["bitdepth"] > 8]
|
||||
|
||||
# Values per target row
|
||||
vpr = width * targetplanes
|
||||
|
||||
def iterasgrey():
|
||||
for i, row in enumerate(rows):
|
||||
row = array.array(typecode, row)
|
||||
targetrow = array.array(typecode, [0] * vpr)
|
||||
# Copy G (and possibly A) channel.
|
||||
green = row[0::planes]
|
||||
if alpha:
|
||||
targetrow[0::2] = green
|
||||
targetrow[1::2] = row[3::4]
|
||||
else:
|
||||
targetrow = green
|
||||
# Check R and B channel match.
|
||||
if green != row[0::planes] or green != row[2::planes]:
|
||||
raise ValueError("Row %i contains non-grey pixel." % i)
|
||||
yield targetrow
|
||||
|
||||
info["greyscale"] = True
|
||||
del info["planes"]
|
||||
w = png.Writer(**info)
|
||||
return w.write(out, iterasgrey())
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return as_grey(png.binary_stdout(), args.input)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
sys.exit(main())
|
||||
111
Backend/venv/bin/pripalpng
Executable file
111
Backend/venv/bin/pripalpng
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
# pripalpng
|
||||
|
||||
|
||||
"""Convert to Palette PNG (without changing colours)"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
|
||||
# https://docs.python.org/2.7/library/io.html
|
||||
import io
|
||||
import string
|
||||
import zlib
|
||||
|
||||
# Local module.
|
||||
import png
|
||||
|
||||
|
||||
def make_inverse_palette(rows, channels):
|
||||
"""
|
||||
The inverse palette maps from tuple to palette index.
|
||||
"""
|
||||
|
||||
palette = {}
|
||||
|
||||
for row in rows:
|
||||
for pixel in png.group(row, channels):
|
||||
if pixel in palette:
|
||||
continue
|
||||
palette[pixel] = len(palette)
|
||||
return palette
|
||||
|
||||
|
||||
def palette_convert(out, inp, palette_file):
|
||||
"""
|
||||
Convert PNG image in `inp` to use a palette, colour type 3,
|
||||
and write converted image to `out`.
|
||||
|
||||
`palette_file` is a file descriptor for the palette to use.
|
||||
|
||||
If `palette_file` is None, then `inp` is used as the palette.
|
||||
"""
|
||||
|
||||
if palette_file is None:
|
||||
inp, palette_file = palette_file, inp
|
||||
|
||||
reader = png.Reader(file=palette_file)
|
||||
w, h, rows, info = asRGBorA8(reader)
|
||||
channels = info["planes"]
|
||||
if not inp:
|
||||
rows = list(rows)
|
||||
|
||||
palette_map = make_inverse_palette(rows, channels)
|
||||
|
||||
if inp:
|
||||
reader = png.Reader(file=inp)
|
||||
w, h, rows, info = asRGBorA8(reader)
|
||||
channels = info["planes"]
|
||||
|
||||
# Default for colours not in palette is to use last entry.
|
||||
last = len(palette_map) - 1
|
||||
|
||||
def map_pixel(p):
|
||||
return palette_map.get(p, last)
|
||||
|
||||
def convert_rows():
|
||||
for row in rows:
|
||||
yield [map_pixel(p) for p in png.group(row, channels)]
|
||||
|
||||
# Make a palette by sorting the pixels according to their index.
|
||||
palette = sorted(palette_map.keys(), key=palette_map.get)
|
||||
pal_info = dict(size=info["size"], palette=palette)
|
||||
|
||||
w = png.Writer(**pal_info)
|
||||
w.write(out, convert_rows())
|
||||
|
||||
|
||||
def asRGBorA8(reader):
|
||||
"""
|
||||
Return (width, height, rows, info) converting to RGB,
|
||||
or RGBA if original has an alpha channel.
|
||||
"""
|
||||
_, _, _, info = reader.read()
|
||||
if info["alpha"]:
|
||||
return reader.asRGBA8()
|
||||
else:
|
||||
return reader.asRGB8()
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import sys
|
||||
import re
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
argv = argv[1:]
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--palette", type=png.cli_open)
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
palette_convert(png.binary_stdout(), args.input, args.palette)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
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)
|
||||
540
Backend/venv/bin/priplan9topng
Executable file
540
Backend/venv/bin/priplan9topng
Executable file
@@ -0,0 +1,540 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# Imported from //depot/prj/plan9topam/master/code/plan9topam.py#4 on
|
||||
# 2009-06-15.
|
||||
|
||||
"""Command line tool to convert from Plan 9 image format to PNG format.
|
||||
|
||||
Plan 9 image format description:
|
||||
https://plan9.io/magic/man2html/6/image
|
||||
|
||||
Where possible this tool will use unbuffered read() calls,
|
||||
so that when finished the file offset is exactly at the end of
|
||||
the image data.
|
||||
This is useful for Plan9 subfont files which place font metric
|
||||
data immediately after the image.
|
||||
"""
|
||||
|
||||
# Test materials
|
||||
|
||||
# asset/left.bit is a Plan 9 image file, a leftwards facing Glenda.
|
||||
# Other materials have to be scrounged from the internet.
|
||||
# https://plan9.io/sources/plan9/sys/games/lib/sokoban/images/cargo.bit
|
||||
|
||||
import array
|
||||
import collections
|
||||
import io
|
||||
|
||||
# http://www.python.org/doc/2.3.5/lib/module-itertools.html
|
||||
import itertools
|
||||
import os
|
||||
|
||||
# http://www.python.org/doc/2.3.5/lib/module-re.html
|
||||
import re
|
||||
import struct
|
||||
|
||||
# http://www.python.org/doc/2.3.5/lib/module-sys.html
|
||||
import sys
|
||||
|
||||
# https://docs.python.org/3/library/tarfile.html
|
||||
import tarfile
|
||||
|
||||
|
||||
# https://pypi.org/project/pypng/
|
||||
import png
|
||||
|
||||
# internal
|
||||
import prix
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Some sort of Plan 9 image error."""
|
||||
|
||||
|
||||
def block(s, n):
|
||||
return zip(*[iter(s)] * n)
|
||||
|
||||
|
||||
def plan9_as_image(inp):
|
||||
"""Represent a Plan 9 image file as a png.Image instance, so
|
||||
that it can be written as a PNG file.
|
||||
Works with compressed input files and may work with uncompressed files.
|
||||
"""
|
||||
|
||||
# Use inp.raw if available.
|
||||
# This avoids buffering and means that when the image is processed,
|
||||
# the resulting input stream is cued up exactly at the end
|
||||
# of the image.
|
||||
inp = getattr(inp, "raw", inp)
|
||||
|
||||
info, blocks = plan9_open_image(inp)
|
||||
|
||||
rows, infodict = plan9_image_rows(blocks, info)
|
||||
|
||||
return png.Image(rows, infodict)
|
||||
|
||||
|
||||
def plan9_open_image(inp):
|
||||
"""Open a Plan9 image file (`inp` should be an already open
|
||||
file object), and return (`info`, `blocks`) pair.
|
||||
`info` should be a Plan9 5-tuple;
|
||||
`blocks` is the input, and it should yield (`row`, `data`)
|
||||
pairs (see :meth:`pixmeta`).
|
||||
"""
|
||||
|
||||
r = inp.read(11)
|
||||
if r == b"compressed\n":
|
||||
info, blocks = decompress(inp)
|
||||
else:
|
||||
# Since Python 3, there is a good chance that this path
|
||||
# doesn't work.
|
||||
info, blocks = glue(inp, r)
|
||||
|
||||
return info, blocks
|
||||
|
||||
|
||||
def glue(f, r):
|
||||
"""Return (info, stream) pair, given `r` the initial portion of
|
||||
the metadata that has already been read from the stream `f`.
|
||||
"""
|
||||
|
||||
r = r + f.read(60 - len(r))
|
||||
return (meta(r), f)
|
||||
|
||||
|
||||
def meta(r):
|
||||
"""Convert 60 byte bytestring `r`, the metadata from an image file.
|
||||
Returns a 5-tuple (*chan*,*minx*,*miny*,*limx*,*limy*).
|
||||
5-tuples may settle into lists in transit.
|
||||
|
||||
As per https://plan9.io/magic/man2html/6/image the metadata
|
||||
comprises 5 words separated by blanks.
|
||||
As it happens each word starts at an index that is a multiple of 12,
|
||||
but this routine does not care about that.
|
||||
"""
|
||||
|
||||
r = r.split()
|
||||
# :todo: raise FormatError
|
||||
if 5 != len(r):
|
||||
raise Error("Expected 5 space-separated words in metadata")
|
||||
r = [r[0]] + [int(x) for x in r[1:]]
|
||||
return r
|
||||
|
||||
|
||||
def bitdepthof(chan):
|
||||
"""Return the bitdepth for a Plan9 pixel format string."""
|
||||
|
||||
maxd = 0
|
||||
for c in re.findall(rb"[a-z]\d*", chan):
|
||||
if c[0] != "x":
|
||||
maxd = max(maxd, int(c[1:]))
|
||||
return maxd
|
||||
|
||||
|
||||
def maxvalof(chan):
|
||||
"""Return the netpbm MAXVAL for a Plan9 pixel format string."""
|
||||
|
||||
bitdepth = bitdepthof(chan)
|
||||
return (2 ** bitdepth) - 1
|
||||
|
||||
|
||||
def plan9_image_rows(blocks, metadata):
|
||||
"""
|
||||
Convert (uncompressed) Plan 9 image file to pair of (*rows*, *info*).
|
||||
This is intended to be used by PyPNG format.
|
||||
*info* is the image info (metadata) returned in a dictionary,
|
||||
*rows* is an iterator that yields each row in
|
||||
boxed row flat pixel format.
|
||||
|
||||
`blocks`, should be an iterator of (`row`, `data`) pairs.
|
||||
"""
|
||||
|
||||
chan, minx, miny, limx, limy = metadata
|
||||
rows = limy - miny
|
||||
width = limx - minx
|
||||
nchans = len(re.findall(b"[a-wyz]", chan))
|
||||
alpha = b"a" in chan
|
||||
# Iverson's convention for the win!
|
||||
ncolour = nchans - alpha
|
||||
greyscale = ncolour == 1
|
||||
bitdepth = bitdepthof(chan)
|
||||
maxval = maxvalof(chan)
|
||||
|
||||
# PNG style info dict.
|
||||
meta = dict(
|
||||
size=(width, rows),
|
||||
bitdepth=bitdepth,
|
||||
greyscale=greyscale,
|
||||
alpha=alpha,
|
||||
planes=nchans,
|
||||
)
|
||||
|
||||
arraycode = "BH"[bitdepth > 8]
|
||||
|
||||
return (
|
||||
map(
|
||||
lambda x: array.array(arraycode, itertools.chain(*x)),
|
||||
block(unpack(blocks, rows, width, chan, maxval), width),
|
||||
),
|
||||
meta,
|
||||
)
|
||||
|
||||
|
||||
def unpack(f, rows, width, chan, maxval):
|
||||
"""Unpack `f` into pixels.
|
||||
`chan` describes the pixel format using
|
||||
the Plan9 syntax ("k8", "r8g8b8", and so on).
|
||||
Assumes the pixel format has a total channel bit depth
|
||||
that is either a multiple or a divisor of 8
|
||||
(the Plan9 image specification requires this).
|
||||
`f` should be an iterator that returns blocks of input such that
|
||||
each block contains a whole number of pixels.
|
||||
The return value is an iterator that yields each pixel as an n-tuple.
|
||||
"""
|
||||
|
||||
def mask(w):
|
||||
"""An integer, to be used as a mask, with bottom `w` bits set to 1."""
|
||||
|
||||
return (1 << w) - 1
|
||||
|
||||
def deblock(f, depth, width):
|
||||
"""A "packer" used to convert multiple bytes into single pixels.
|
||||
`depth` is the pixel depth in bits (>= 8), `width` is the row width in
|
||||
pixels.
|
||||
"""
|
||||
|
||||
w = depth // 8
|
||||
i = 0
|
||||
for block in f:
|
||||
for i in range(len(block) // w):
|
||||
p = block[w * i : w * (i + 1)]
|
||||
i += w
|
||||
# Convert little-endian p to integer x
|
||||
x = 0
|
||||
s = 1 # scale
|
||||
for j in p:
|
||||
x += s * j
|
||||
s <<= 8
|
||||
yield x
|
||||
|
||||
def bitfunge(f, depth, width):
|
||||
"""A "packer" used to convert single bytes into multiple pixels.
|
||||
Depth is the pixel depth (< 8), width is the row width in pixels.
|
||||
"""
|
||||
|
||||
assert 8 / depth == 8 // depth
|
||||
|
||||
for block in f:
|
||||
col = 0
|
||||
for x in block:
|
||||
for j in range(8 // depth):
|
||||
yield x >> (8 - depth)
|
||||
col += 1
|
||||
if col == width:
|
||||
# A row-end forces a new byte even if
|
||||
# we haven't consumed all of the current byte.
|
||||
# Effectively rows are bit-padded to make
|
||||
# a whole number of bytes.
|
||||
col = 0
|
||||
break
|
||||
x <<= depth
|
||||
|
||||
# number of bits in each channel
|
||||
bits = [int(d) for d in re.findall(rb"\d+", chan)]
|
||||
# colr of each channel
|
||||
# (r, g, b, k for actual colours, and
|
||||
# a, m, x for alpha, map-index, and unused)
|
||||
colr = re.findall(b"[a-z]", chan)
|
||||
|
||||
depth = sum(bits)
|
||||
|
||||
# Select a "packer" that either:
|
||||
# - gathers multiple bytes into a single pixel (for depth >= 8); or,
|
||||
# - splits bytes into several pixels (for depth < 8).
|
||||
if depth >= 8:
|
||||
assert depth % 8 == 0
|
||||
packer = deblock
|
||||
else:
|
||||
assert 8 % depth == 0
|
||||
packer = bitfunge
|
||||
|
||||
for x in packer(f, depth, width):
|
||||
# x is the pixel as an unsigned integer
|
||||
o = []
|
||||
# This is a bit yucky.
|
||||
# Extract each channel from the _most_ significant part of x.
|
||||
for b, col in zip(bits, colr):
|
||||
v = (x >> (depth - b)) & mask(b)
|
||||
x <<= b
|
||||
if col != "x":
|
||||
# scale to maxval
|
||||
v = v * float(maxval) / mask(b)
|
||||
v = int(v + 0.5)
|
||||
o.append(v)
|
||||
yield o
|
||||
|
||||
|
||||
def decompress(f):
|
||||
"""Decompress a Plan 9 image file.
|
||||
The input `f` should be a binary file object that
|
||||
is already cued past the initial 'compressed\n' string.
|
||||
The return result is (`info`, `blocks`);
|
||||
`info` is a 5-tuple of the Plan 9 image metadata;
|
||||
`blocks` is an iterator that yields a (row, data) pair
|
||||
for each block of data.
|
||||
"""
|
||||
|
||||
r = meta(f.read(60))
|
||||
return r, decomprest(f, r[4])
|
||||
|
||||
|
||||
def decomprest(f, rows):
|
||||
"""Iterator that decompresses the rest of a file once the metadata
|
||||
have been consumed."""
|
||||
|
||||
row = 0
|
||||
while row < rows:
|
||||
row, o = deblock(f)
|
||||
yield o
|
||||
|
||||
|
||||
def deblock(f):
|
||||
"""Decompress a single block from a compressed Plan 9 image file.
|
||||
Each block starts with 2 decimal strings of 12 bytes each.
|
||||
Yields a sequence of (row, data) pairs where
|
||||
`row` is the total number of rows processed
|
||||
(according to the file format) and
|
||||
`data` is the decompressed data for this block.
|
||||
"""
|
||||
|
||||
row = int(f.read(12))
|
||||
size = int(f.read(12))
|
||||
if not (0 <= size <= 6000):
|
||||
raise Error("block has invalid size; not a Plan 9 image file?")
|
||||
|
||||
# Since each block is at most 6000 bytes we may as well read it all in
|
||||
# one go.
|
||||
d = f.read(size)
|
||||
i = 0
|
||||
o = []
|
||||
|
||||
while i < size:
|
||||
x = d[i]
|
||||
i += 1
|
||||
if x & 0x80:
|
||||
x = (x & 0x7F) + 1
|
||||
lit = d[i : i + x]
|
||||
i += x
|
||||
o.extend(lit)
|
||||
continue
|
||||
# x's high-order bit is 0
|
||||
length = (x >> 2) + 3
|
||||
# Offset is made from bottom 2 bits of x and 8 bits of next byte.
|
||||
# MSByte LSByte
|
||||
# +---------------------+-------------------------+
|
||||
# | - - - - - - | x1 x0 | d7 d6 d5 d4 d3 d2 d1 d0 |
|
||||
# +-----------------------------------------------+
|
||||
# Had to discover by inspection which way round the bits go,
|
||||
# because https://plan9.io/magic/man2html/6/image doesn't say.
|
||||
# that x's 2 bits are most significant.
|
||||
offset = (x & 3) << 8
|
||||
offset |= d[i]
|
||||
i += 1
|
||||
# Note: complement operator neatly maps (0 to 1023) to (-1 to
|
||||
# -1024). Adding len(o) gives a (non-negative) offset into o from
|
||||
# which to start indexing.
|
||||
offset = ~offset + len(o)
|
||||
if offset < 0:
|
||||
raise Error(
|
||||
"byte offset indexes off the begininning of "
|
||||
"the output buffer; not a Plan 9 image file?"
|
||||
)
|
||||
for j in range(length):
|
||||
o.append(o[offset + j])
|
||||
return row, bytes(o)
|
||||
|
||||
|
||||
FontChar = collections.namedtuple("FontChar", "x top bottom left width")
|
||||
|
||||
|
||||
def font_copy(inp, image, out, control):
|
||||
"""
|
||||
Convert a Plan 9 font (`inp`, `image`) to a series of PNG images,
|
||||
and write them out as a tar file to the file object `out`.
|
||||
Write a text control file out to the file object `control`.
|
||||
|
||||
Each valid glyph in the font becomes a single PNG image;
|
||||
the output is a tar file of all the images.
|
||||
|
||||
A Plan 9 font consists of a Plan 9 image immediately
|
||||
followed by font data.
|
||||
The image for the font should be the `image` argument,
|
||||
the file containing the rest of the font data should be the
|
||||
file object `inp` which should be cued up to the start of
|
||||
the font data that immediately follows the image.
|
||||
|
||||
https://plan9.io/magic/man2html/6/font
|
||||
"""
|
||||
|
||||
# The format is a little unusual, and isn't completely
|
||||
# clearly documented.
|
||||
# Each 6-byte structure (see FontChar above) defines
|
||||
# a rectangular region of the image that is used for each
|
||||
# glyph.
|
||||
# The source image region that is used may be strictly
|
||||
# smaller than the rectangle for the target glyph.
|
||||
# This seems like a micro-optimisation.
|
||||
# For each glyph,
|
||||
# rows above `top` and below `bottom` will not be copied
|
||||
# from the source (they can be assumed to be blank).
|
||||
# No space is saved in the source image, since the rows must
|
||||
# be present.
|
||||
# `x` is always non-decreasing, so the glyphs appear strictly
|
||||
# left-to-image in the source image.
|
||||
# The x of the next glyph is used to
|
||||
# infer the width of the source rectangle.
|
||||
# `top` and `bottom` give the y-coordinate of the top- and
|
||||
# bottom- sides of the rectangle in both source and targets.
|
||||
# `left` is the x-coordinate of the left-side of the
|
||||
# rectangle in the target glyph. (equivalently, the amount
|
||||
# of padding that should be added on the left).
|
||||
# `width` is the advance-width of the glyph; by convention
|
||||
# it is 0 for an undefined glyph.
|
||||
|
||||
name = getattr(inp, "name", "*subfont*name*not*supplied*")
|
||||
|
||||
header = inp.read(36)
|
||||
n, height, ascent = [int(x) for x in header.split()]
|
||||
print("baseline", name, ascent, file=control, sep=",")
|
||||
|
||||
chs = []
|
||||
for i in range(n + 1):
|
||||
bs = inp.read(6)
|
||||
ch = FontChar(*struct.unpack("<HBBBB", bs))
|
||||
chs.append(ch)
|
||||
|
||||
tar = tarfile.open(mode="w|", fileobj=out)
|
||||
|
||||
# Start at 0, increment for every image output
|
||||
# (recall that not every input glyph has an output image)
|
||||
output_index = 0
|
||||
for i in range(n):
|
||||
ch = chs[i]
|
||||
if ch.width == 0:
|
||||
continue
|
||||
|
||||
print("png", "index", output_index, "glyph", name, i, file=control, sep=",")
|
||||
|
||||
info = dict(image.info, size=(ch.width, height))
|
||||
target = new_image(info)
|
||||
|
||||
source_width = chs[i + 1].x - ch.x
|
||||
rect = ((ch.left, ch.top), (ch.left + source_width, ch.bottom))
|
||||
image_draw(target, rect, image, (ch.x, ch.top))
|
||||
|
||||
# :todo: add source, glyph, and baseline data here (as a
|
||||
# private tag?)
|
||||
o = io.BytesIO()
|
||||
target.write(o)
|
||||
binary_size = o.tell()
|
||||
o.seek(0)
|
||||
|
||||
tarinfo = tar.gettarinfo(arcname="%s/glyph%d.png" % (name, i), fileobj=inp)
|
||||
tarinfo.size = binary_size
|
||||
tar.addfile(tarinfo, fileobj=o)
|
||||
|
||||
output_index += 1
|
||||
|
||||
tar.close()
|
||||
|
||||
|
||||
def new_image(info):
|
||||
"""Return a fresh png.Image instance."""
|
||||
|
||||
width, height = info["size"]
|
||||
vpr = width * info["planes"]
|
||||
row = lambda: [0] * vpr
|
||||
rows = [row() for _ in range(height)]
|
||||
return png.Image(rows, info)
|
||||
|
||||
|
||||
def image_draw(target, rect, source, point):
|
||||
"""The point `point` in the source image is aligned with the
|
||||
top-left of rect in the target image, and then the rectangle
|
||||
in target is replaced with the pixels from `source`.
|
||||
|
||||
This routine assumes that both source and target can have
|
||||
their rows objects indexed (not streamed).
|
||||
"""
|
||||
|
||||
# :todo: there is no attempt to do clipping or channel or
|
||||
# colour conversion. But maybe later?
|
||||
|
||||
if target.info["planes"] != source.info["planes"]:
|
||||
raise NotImplementedError(
|
||||
"source and target must have the same number of planes"
|
||||
)
|
||||
|
||||
if target.info["bitdepth"] != source.info["bitdepth"]:
|
||||
raise NotImplementedError("source and target must have the same bitdepth")
|
||||
|
||||
tl, br = rect
|
||||
left, top = tl
|
||||
right, bottom = br
|
||||
height = bottom - top
|
||||
|
||||
planes = source.info["planes"]
|
||||
|
||||
vpr = (right - left) * planes
|
||||
source_left, source_top = point
|
||||
|
||||
source_l = source_left * planes
|
||||
source_r = source_l + vpr
|
||||
|
||||
target_l = left * planes
|
||||
target_r = target_l + vpr
|
||||
|
||||
for y in range(height):
|
||||
row = source.rows[y + source_top]
|
||||
row = row[source_l:source_r]
|
||||
target.rows[top + y][target_l:target_r] = row
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Convert Plan9 image to PNG")
|
||||
parser.add_argument(
|
||||
"input",
|
||||
nargs="?",
|
||||
default="-",
|
||||
type=png.cli_open,
|
||||
metavar="image",
|
||||
help="image file in Plan 9 format",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--control",
|
||||
default=os.path.devnull,
|
||||
type=argparse.FileType("w"),
|
||||
metavar="ControlCSV",
|
||||
help="(when using --font) write a control CSV file to named file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--font",
|
||||
action="store_true",
|
||||
help="process as Plan 9 subfont: output a tar file of PNGs",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
image = plan9_as_image(args.input)
|
||||
image.stream()
|
||||
|
||||
if not args.font:
|
||||
image.write(png.binary_stdout())
|
||||
else:
|
||||
font_copy(args.input, image, png.binary_stdout(), args.control)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
33
Backend/venv/bin/pripnglsch
Executable file
33
Backend/venv/bin/pripnglsch
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
# pripnglsch
|
||||
# PNG List Chunks
|
||||
|
||||
import png
|
||||
|
||||
|
||||
def list_chunks(out, inp):
|
||||
r = png.Reader(file=inp)
|
||||
for t, v in r.chunks():
|
||||
add = ""
|
||||
if len(v) <= 28:
|
||||
add = " " + v.hex()
|
||||
else:
|
||||
add = " " + v[:26].hex() + "..."
|
||||
t = t.decode("ascii")
|
||||
print("%s %10d%s" % (t, len(v), add), file=out)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return list_chunks(sys.stdout, args.input)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
101
Backend/venv/bin/pripngtopam
Executable file
101
Backend/venv/bin/pripngtopam
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
import struct
|
||||
|
||||
import png
|
||||
|
||||
|
||||
def write_pnm(file, plain, rows, meta):
|
||||
"""
|
||||
Write a Netpbm PNM (or PAM) file.
|
||||
*file* output file object;
|
||||
*plain* (a bool) true if writing plain format (not possible for PAM);
|
||||
*rows* an iterator for the rows;
|
||||
*meta* the info dictionary.
|
||||
"""
|
||||
|
||||
meta = dict(meta)
|
||||
meta["maxval"] = 2 ** meta["bitdepth"] - 1
|
||||
meta["width"], meta["height"] = meta["size"]
|
||||
|
||||
# Number of planes determines both image formats:
|
||||
# 1 : L to PGM
|
||||
# 2 : LA to PAM
|
||||
# 3 : RGB to PPM
|
||||
# 4 : RGBA to PAM
|
||||
planes = meta["planes"]
|
||||
|
||||
# Assume inputs are from a PNG file.
|
||||
assert planes in (1, 2, 3, 4)
|
||||
if planes in (1, 3):
|
||||
if 1 == planes:
|
||||
# PGM
|
||||
# Even if maxval is 1 we use PGM instead of PBM,
|
||||
# to avoid converting data.
|
||||
magic = "P5"
|
||||
if plain:
|
||||
magic = "P2"
|
||||
else:
|
||||
# PPM
|
||||
magic = "P6"
|
||||
if plain:
|
||||
magic = "P3"
|
||||
header = "{magic} {width:d} {height:d} {maxval:d}\n".format(magic=magic, **meta)
|
||||
if planes in (2, 4):
|
||||
# PAM
|
||||
# See http://netpbm.sourceforge.net/doc/pam.html
|
||||
if plain:
|
||||
raise Exception("PAM (%d-plane) does not support plain format" % planes)
|
||||
if 2 == planes:
|
||||
tupltype = "GRAYSCALE_ALPHA"
|
||||
else:
|
||||
tupltype = "RGB_ALPHA"
|
||||
header = (
|
||||
"P7\nWIDTH {width:d}\nHEIGHT {height:d}\n"
|
||||
"DEPTH {planes:d}\nMAXVAL {maxval:d}\n"
|
||||
"TUPLTYPE {tupltype}\nENDHDR\n".format(tupltype=tupltype, **meta)
|
||||
)
|
||||
file.write(header.encode("ascii"))
|
||||
|
||||
# Values per row
|
||||
vpr = planes * meta["width"]
|
||||
|
||||
if plain:
|
||||
for row in rows:
|
||||
row_b = b" ".join([b"%d" % v for v in row])
|
||||
file.write(row_b)
|
||||
file.write(b"\n")
|
||||
else:
|
||||
# format for struct.pack
|
||||
fmt = ">%d" % vpr
|
||||
if meta["maxval"] > 0xFF:
|
||||
fmt = fmt + "H"
|
||||
else:
|
||||
fmt = fmt + "B"
|
||||
for row in rows:
|
||||
file.write(struct.pack(fmt, *row))
|
||||
|
||||
file.flush()
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Convert PNG to PAM")
|
||||
parser.add_argument("--plain", action="store_true")
|
||||
parser.add_argument(
|
||||
"input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Encode PNG to PNM (or PAM)
|
||||
image = png.Reader(file=args.input)
|
||||
_, _, rows, info = image.asDirect()
|
||||
write_pnm(png.binary_stdout(), args.plain, rows, info)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
sys.exit(main())
|
||||
71
Backend/venv/bin/prirowpng
Executable file
71
Backend/venv/bin/prirowpng
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# http://www.python.org/doc/2.4.4/lib/module-itertools.html
|
||||
import itertools
|
||||
import sys
|
||||
|
||||
import png
|
||||
|
||||
Description = """Join PNG images in a row left-to-right."""
|
||||
|
||||
|
||||
class FormatError(Exception):
|
||||
"""
|
||||
Some problem with the image format.
|
||||
"""
|
||||
|
||||
|
||||
def join_row(out, l):
|
||||
"""
|
||||
Concatenate the list of images.
|
||||
All input images must be same height and
|
||||
have the same number of channels.
|
||||
They are concatenated left-to-right.
|
||||
`out` is the (open file) destination for the output image.
|
||||
`l` should be a list of open files (the input image files).
|
||||
"""
|
||||
|
||||
l = [png.Reader(file=f) for f in l]
|
||||
|
||||
# Ewgh, side effects.
|
||||
for r in l:
|
||||
r.preamble()
|
||||
|
||||
# The reference height; from the first image.
|
||||
height = l[0].height
|
||||
# The total target width
|
||||
width = 0
|
||||
for i,r in enumerate(l):
|
||||
if r.height != height:
|
||||
raise FormatError('Image %d, height %d, does not match %d.' %
|
||||
(i, r.height, height))
|
||||
width += r.width
|
||||
|
||||
# Various bugs here because different numbers of channels and depths go wrong.
|
||||
pixel, info = zip(*[r.asDirect()[2:4] for r in l])
|
||||
tinfo = dict(info[0])
|
||||
del tinfo['size']
|
||||
w = png.Writer(width, height, **tinfo)
|
||||
|
||||
def iter_all_rows():
|
||||
for row in zip(*pixel):
|
||||
# `row` is a sequence that has one row from each input image.
|
||||
# list() is required here to hasten the lazy row building;
|
||||
# not sure if that's a bug in PyPNG or not.
|
||||
yield list(itertools.chain(*row))
|
||||
w.write(out, iter_all_rows())
|
||||
|
||||
def main(argv):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description=Description)
|
||||
parser.add_argument(
|
||||
"input", nargs="*", default="-", type=png.cli_open, metavar="PNG"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return join_row(png.binary_stdout(), args.input)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
215
Backend/venv/bin/priweavepng
Executable file
215
Backend/venv/bin/priweavepng
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
|
||||
# priweavepng
|
||||
# Weave selected channels from input PNG files into
|
||||
# a multi-channel output PNG.
|
||||
|
||||
import collections
|
||||
import re
|
||||
|
||||
from array import array
|
||||
|
||||
import png
|
||||
|
||||
"""
|
||||
priweavepng file1.png [file2.png ...]
|
||||
|
||||
The `priweavepng` tool combines channels from the input images and
|
||||
weaves a selection of those channels into an output image.
|
||||
|
||||
Conceptually an intermediate image is formed consisting of
|
||||
all channels of all input images in the order given on the command line
|
||||
and in the order of each channel in its image.
|
||||
Then from 1 to 4 channels are selected and
|
||||
an image is output with those channels.
|
||||
The limit on the number of selected channels is
|
||||
imposed by the PNG image format.
|
||||
|
||||
The `-c n` option selects channel `n`.
|
||||
Further channels can be selected either by repeating the `-c` option,
|
||||
or using a comma separated list.
|
||||
For example `-c 3,2,1` will select channels 3, 2, and 1 in that order;
|
||||
if the input is an RGB PNG, this will swop the Red and Blue channels.
|
||||
The order is significant, the order in which the options are given is
|
||||
the order of the output channels.
|
||||
It is permissible, and sometimes useful
|
||||
(for example, grey to colour expansion, see below),
|
||||
to repeat the same channel.
|
||||
|
||||
If no `-c` option is used the default is
|
||||
to select all of the input channels, up to the first 4.
|
||||
|
||||
`priweavepng` does not care about the meaning of the channels
|
||||
and treats them as a matrix of values.
|
||||
|
||||
The numer of output channels determines the colour mode of the PNG file:
|
||||
L (1-channel, Grey), LA (2-channel, Grey+Alpha),
|
||||
RGB (3-channel, Red+Green+Blue), RGBA (4-channel, Red+Green+Blue+Alpha).
|
||||
|
||||
The `priweavepng` tool can be used for a variety of
|
||||
channel building, swopping, and extraction effects:
|
||||
|
||||
Combine 3 grayscale images into RGB colour:
|
||||
priweavepng grey1.png grey2.png grey3.png
|
||||
|
||||
Swop Red and Blue channels in colour image:
|
||||
priweavepng -c 3 -c 2 -c 1 rgb.png
|
||||
|
||||
Extract Green channel as a greyscale image:
|
||||
priweavepng -c 2 rgb.png
|
||||
|
||||
Convert a greyscale image to a colour image (all grey):
|
||||
priweavepng -c 1 -c 1 -c 1 grey.png
|
||||
|
||||
Add alpha mask from a separate (greyscale) image:
|
||||
priweavepng rgb.png grey.png
|
||||
|
||||
Extract alpha mask into a separate (greyscale) image:
|
||||
priweavepng -c 4 rgba.png
|
||||
|
||||
Steal alpha mask from second file and add to first.
|
||||
Note that the intermediate image in this example has 7 channels:
|
||||
priweavepng -c 1 -c 2 -c 3 -c 7 rgb.png rgba.png
|
||||
|
||||
Take Green channel from 3 successive colour images to make a new RGB image:
|
||||
priweavepng -c 2 -c 5 -c 8 rgb1.png rgb2.png rgb3.png
|
||||
|
||||
"""
|
||||
|
||||
Image = collections.namedtuple("Image", "rows info")
|
||||
|
||||
# For each channel in the intermediate raster,
|
||||
# model:
|
||||
# - image: the input image (0-based);
|
||||
# - i: the channel index within that image (0-based);
|
||||
# - bitdepth: the bitdepth of this channel.
|
||||
Channel = collections.namedtuple("Channel", "image i bitdepth")
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def weave(out, args):
|
||||
"""Stack the input PNG files and extract channels
|
||||
into a single output PNG.
|
||||
"""
|
||||
|
||||
paths = args.input
|
||||
|
||||
if len(paths) < 1:
|
||||
raise Error("Required input is missing.")
|
||||
|
||||
# List of Image instances
|
||||
images = []
|
||||
# Channel map. Maps from channel number (starting from 1)
|
||||
# to an (image_index, channel_index) pair.
|
||||
channel_map = dict()
|
||||
channel = 1
|
||||
|
||||
for image_index, path in enumerate(paths):
|
||||
inp = png.cli_open(path)
|
||||
rows, info = png.Reader(file=inp).asDirect()[2:]
|
||||
rows = list(rows)
|
||||
image = Image(rows, info)
|
||||
images.append(image)
|
||||
# A later version of PyPNG may intelligently support
|
||||
# PNG files with heterogenous bitdepths.
|
||||
# For now, assumes bitdepth of all channels in image
|
||||
# is the same.
|
||||
channel_bitdepth = (image.info["bitdepth"],) * image.info["planes"]
|
||||
for i in range(image.info["planes"]):
|
||||
channel_map[channel + i] = Channel(image_index, i, channel_bitdepth[i])
|
||||
channel += image.info["planes"]
|
||||
|
||||
assert channel - 1 == sum(image.info["planes"] for image in images)
|
||||
|
||||
# If no channels, select up to first 4 as default.
|
||||
if not args.channel:
|
||||
args.channel = range(1, channel)[:4]
|
||||
|
||||
out_channels = len(args.channel)
|
||||
if not (0 < out_channels <= 4):
|
||||
raise Error("Too many channels selected (must be 1 to 4)")
|
||||
alpha = out_channels in (2, 4)
|
||||
greyscale = out_channels in (1, 2)
|
||||
|
||||
bitdepth = tuple(image.info["bitdepth"] for image in images)
|
||||
arraytype = "BH"[max(bitdepth) > 8]
|
||||
|
||||
size = [image.info["size"] for image in images]
|
||||
# Currently, fail unless all images same size.
|
||||
if len(set(size)) > 1:
|
||||
raise NotImplementedError("Cannot cope when sizes differ - sorry!")
|
||||
size = size[0]
|
||||
|
||||
# Values per row, of output image
|
||||
vpr = out_channels * size[0]
|
||||
|
||||
def weave_row_iter():
|
||||
"""
|
||||
Yield each woven row in turn.
|
||||
"""
|
||||
# The zip call creates an iterator that yields
|
||||
# a tuple with each element containing the next row
|
||||
# for each of the input images.
|
||||
for row_tuple in zip(*(image.rows for image in images)):
|
||||
# output row
|
||||
row = array(arraytype, [0] * vpr)
|
||||
# for each output channel select correct input channel
|
||||
for out_channel_i, selection in enumerate(args.channel):
|
||||
channel = channel_map[selection]
|
||||
# incoming row (make it an array)
|
||||
irow = array(arraytype, row_tuple[channel.image])
|
||||
n = images[channel.image].info["planes"]
|
||||
row[out_channel_i::out_channels] = irow[channel.i :: n]
|
||||
yield row
|
||||
|
||||
w = png.Writer(
|
||||
size[0],
|
||||
size[1],
|
||||
greyscale=greyscale,
|
||||
alpha=alpha,
|
||||
bitdepth=bitdepth,
|
||||
interlace=args.interlace,
|
||||
)
|
||||
w.write(out, weave_row_iter())
|
||||
|
||||
|
||||
def comma_list(s):
|
||||
"""
|
||||
Type and return a list of integers.
|
||||
"""
|
||||
|
||||
return [int(c) for c in re.findall(r"\d+", s)]
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import argparse
|
||||
import itertools
|
||||
import sys
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
argv = argv[1:]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--channel",
|
||||
action="append",
|
||||
type=comma_list,
|
||||
help="list of channels to extract",
|
||||
)
|
||||
parser.add_argument("--interlace", action="store_true", help="write interlaced PNG")
|
||||
parser.add_argument("input", nargs="+")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.channel:
|
||||
args.channel = list(itertools.chain(*args.channel))
|
||||
|
||||
return weave(png.binary_stdout(), args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
Backend/venv/bin/qr
Executable file
7
Backend/venv/bin/qr
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/home/gnx/Desktop/Hotel-Booking/Backend/venv/bin/python3
|
||||
import sys
|
||||
from qrcode.console_scripts import main
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[0].endswith('.exe'):
|
||||
sys.argv[0] = sys.argv[0][:-4]
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user