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

1#!/usr/bin/env python3 

2 

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]_). 

11 

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``. 

18 

19 Returns 

20 ------- 

21 stream : bytearray 

22 The PNG image data stream. 

23 

24 Notes 

25 ----- 

26 

27 Copyright 2017 Thomas Guymer [1]_ 

28 

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 """ 

34 

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 

40 

41 # ************************************************************************** 

42 

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" 

55 

56 # ************************************************************************** 

57 

58 # Create short-hands ... 

59 ny, nx, nc = arrUint8.shape 

60 

61 # Initialize array and bytearray ... 

62 scanline = numpy.zeros( 

63 (nc, nx), 

64 dtype = numpy.uint8, 

65 ) 

66 stream = bytearray() 

67 

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() 

86 

87 # Return answer ... 

88 return stream