Coverage for pyguymer3/image/makePngSrc/createStreamAverage.py: 85%
34 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def createStreamAverage(
5 arrUint8,
6 arrInt16,
7 /,
8) -> bytearray:
9 """Create a PNG image data stream of an image using the "average" filter (as
10 defined in the PNG specification [2]_).
12 Parameters
13 ----------
14 arrUint8 : numpy.ndarray
15 A "height * width * colour" unsigned 8-bit integer NumPy array.
16 arrInt16 : numpy.ndarray
17 A signed 16-bit integer NumPy array copy of ``arrUint8``.
19 Returns
20 -------
21 stream : bytearray
22 The PNG image data stream.
24 Notes
25 -----
27 Copyright 2017 Thomas Guymer [1]_
29 References
30 ----------
31 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
32 .. [2] PNG Specification (Third Edition), https://www.w3.org/TR/png-3/
33 """
35 # Import special modules ...
36 try:
37 import numpy
38 except:
39 raise Exception("\"numpy\" is not installed; run \"pip install --user numpy\"") from None
41 # **************************************************************************
43 # Check input ...
44 assert arrUint8.dtype == "uint8", f"the NumPy array is not 8-bit (\"{arrUint8.dtype}\")"
45 assert arrInt16.dtype == "int16", f"the NumPy array is not 16-bit (\"{arrInt16.dtype}\")"
46 assert arrUint8.ndim == 3, f"the NumPy array is not 3D (\"{arrUint8.ndim:d}\")"
47 match arrUint8.shape[2]:
48 case 1:
49 pass
50 case 3:
51 pass
52 case _:
53 raise ValueError(f"the NumPy array does not have either 1 or 3 colour channels (\"{arrUint8.shape[2]:d}\")") from None
54 assert arrUint8.shape == arrInt16.shape, "the NumPy arrays do not have the same shape"
56 # **************************************************************************
58 # Create short-hands ...
59 ny, nx, nc = arrUint8.shape
61 # Initialize array and bytearray ...
62 scanline = numpy.zeros(
63 (nc, nx),
64 dtype = numpy.uint8,
65 )
66 stream = bytearray()
68 # Loop over scanlines ...
69 for iy in range(ny):
70 # Calculate stream for "average" filter ...
71 stream += numpy.uint8(3).tobytes()
72 for ix in range(nx):
73 for ic in range(nc):
74 if ix == 0:
75 p1 = numpy.int16(0)
76 else:
77 p1 = arrInt16[iy, ix - 1, ic]
78 if iy == 0:
79 p2 = numpy.int16(0)
80 else:
81 p2 = arrInt16[iy - 1, ix, ic]
82 diff = arrInt16[iy, ix, ic] - ((p1 + p2) // numpy.int16(2))
83 diff = numpy.mod(diff, 256)
84 scanline[ic, ix] = diff.astype(numpy.uint8)
85 stream += scanline[:, ix].tobytes()
87 # Return answer ...
88 return stream