gooderp18绿色标准版
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

216 行
6.5KB

  1. #!C:\odoobuild\WinPy64\python-3.12.3.amd64\python.exe
  2. # priweavepng
  3. # Weave selected channels from input PNG files into
  4. # a multi-channel output PNG.
  5. import collections
  6. import re
  7. from array import array
  8. import png
  9. """
  10. priweavepng file1.png [file2.png ...]
  11. The `priweavepng` tool combines channels from the input images and
  12. weaves a selection of those channels into an output image.
  13. Conceptually an intermediate image is formed consisting of
  14. all channels of all input images in the order given on the command line
  15. and in the order of each channel in its image.
  16. Then from 1 to 4 channels are selected and
  17. an image is output with those channels.
  18. The limit on the number of selected channels is
  19. imposed by the PNG image format.
  20. The `-c n` option selects channel `n`.
  21. Further channels can be selected either by repeating the `-c` option,
  22. or using a comma separated list.
  23. For example `-c 3,2,1` will select channels 3, 2, and 1 in that order;
  24. if the input is an RGB PNG, this will swop the Red and Blue channels.
  25. The order is significant, the order in which the options are given is
  26. the order of the output channels.
  27. It is permissible, and sometimes useful
  28. (for example, grey to colour expansion, see below),
  29. to repeat the same channel.
  30. If no `-c` option is used the default is
  31. to select all of the input channels, up to the first 4.
  32. `priweavepng` does not care about the meaning of the channels
  33. and treats them as a matrix of values.
  34. The numer of output channels determines the colour mode of the PNG file:
  35. L (1-channel, Grey), LA (2-channel, Grey+Alpha),
  36. RGB (3-channel, Red+Green+Blue), RGBA (4-channel, Red+Green+Blue+Alpha).
  37. The `priweavepng` tool can be used for a variety of
  38. channel building, swopping, and extraction effects:
  39. Combine 3 grayscale images into RGB colour:
  40. priweavepng grey1.png grey2.png grey3.png
  41. Swop Red and Blue channels in colour image:
  42. priweavepng -c 3 -c 2 -c 1 rgb.png
  43. Extract Green channel as a greyscale image:
  44. priweavepng -c 2 rgb.png
  45. Convert a greyscale image to a colour image (all grey):
  46. priweavepng -c 1 -c 1 -c 1 grey.png
  47. Add alpha mask from a separate (greyscale) image:
  48. priweavepng rgb.png grey.png
  49. Extract alpha mask into a separate (greyscale) image:
  50. priweavepng -c 4 rgba.png
  51. Steal alpha mask from second file and add to first.
  52. Note that the intermediate image in this example has 7 channels:
  53. priweavepng -c 1 -c 2 -c 3 -c 7 rgb.png rgba.png
  54. Take Green channel from 3 successive colour images to make a new RGB image:
  55. priweavepng -c 2 -c 5 -c 8 rgb1.png rgb2.png rgb3.png
  56. """
  57. Image = collections.namedtuple("Image", "rows info")
  58. # For each channel in the intermediate raster,
  59. # model:
  60. # - image: the input image (0-based);
  61. # - i: the channel index within that image (0-based);
  62. # - bitdepth: the bitdepth of this channel.
  63. Channel = collections.namedtuple("Channel", "image i bitdepth")
  64. class Error(Exception):
  65. pass
  66. def weave(out, args):
  67. """Stack the input PNG files and extract channels
  68. into a single output PNG.
  69. """
  70. paths = args.input
  71. if len(paths) < 1:
  72. raise Error("Required input is missing.")
  73. # List of Image instances
  74. images = []
  75. # Channel map. Maps from channel number (starting from 1)
  76. # to an (image_index, channel_index) pair.
  77. channel_map = dict()
  78. channel = 1
  79. for image_index, path in enumerate(paths):
  80. inp = png.cli_open(path)
  81. rows, info = png.Reader(file=inp).asDirect()[2:]
  82. rows = list(rows)
  83. image = Image(rows, info)
  84. images.append(image)
  85. # A later version of PyPNG may intelligently support
  86. # PNG files with heterogenous bitdepths.
  87. # For now, assumes bitdepth of all channels in image
  88. # is the same.
  89. channel_bitdepth = (image.info["bitdepth"],) * image.info["planes"]
  90. for i in range(image.info["planes"]):
  91. channel_map[channel + i] = Channel(image_index, i, channel_bitdepth[i])
  92. channel += image.info["planes"]
  93. assert channel - 1 == sum(image.info["planes"] for image in images)
  94. # If no channels, select up to first 4 as default.
  95. if not args.channel:
  96. args.channel = range(1, channel)[:4]
  97. out_channels = len(args.channel)
  98. if not (0 < out_channels <= 4):
  99. raise Error("Too many channels selected (must be 1 to 4)")
  100. alpha = out_channels in (2, 4)
  101. greyscale = out_channels in (1, 2)
  102. bitdepth = tuple(image.info["bitdepth"] for image in images)
  103. arraytype = "BH"[max(bitdepth) > 8]
  104. size = [image.info["size"] for image in images]
  105. # Currently, fail unless all images same size.
  106. if len(set(size)) > 1:
  107. raise NotImplementedError("Cannot cope when sizes differ - sorry!")
  108. size = size[0]
  109. # Values per row, of output image
  110. vpr = out_channels * size[0]
  111. def weave_row_iter():
  112. """
  113. Yield each woven row in turn.
  114. """
  115. # The zip call creates an iterator that yields
  116. # a tuple with each element containing the next row
  117. # for each of the input images.
  118. for row_tuple in zip(*(image.rows for image in images)):
  119. # output row
  120. row = array(arraytype, [0] * vpr)
  121. # for each output channel select correct input channel
  122. for out_channel_i, selection in enumerate(args.channel):
  123. channel = channel_map[selection]
  124. # incoming row (make it an array)
  125. irow = array(arraytype, row_tuple[channel.image])
  126. n = images[channel.image].info["planes"]
  127. row[out_channel_i::out_channels] = irow[channel.i :: n]
  128. yield row
  129. w = png.Writer(
  130. size[0],
  131. size[1],
  132. greyscale=greyscale,
  133. alpha=alpha,
  134. bitdepth=bitdepth,
  135. interlace=args.interlace,
  136. )
  137. w.write(out, weave_row_iter())
  138. def comma_list(s):
  139. """
  140. Type and return a list of integers.
  141. """
  142. return [int(c) for c in re.findall(r"\d+", s)]
  143. def main(argv=None):
  144. import argparse
  145. import itertools
  146. import sys
  147. if argv is None:
  148. argv = sys.argv
  149. argv = argv[1:]
  150. parser = argparse.ArgumentParser()
  151. parser.add_argument(
  152. "-c",
  153. "--channel",
  154. action="append",
  155. type=comma_list,
  156. help="list of channels to extract",
  157. )
  158. parser.add_argument("--interlace", action="store_true", help="write interlaced PNG")
  159. parser.add_argument("input", nargs="+")
  160. args = parser.parse_args(argv)
  161. if args.channel:
  162. args.channel = list(itertools.chain(*args.channel))
  163. return weave(png.binary_stdout(), args)
  164. if __name__ == "__main__":
  165. main()
上海开阖软件有限公司 沪ICP备12045867号-1