せっかくのグリーンスクリーンなので、らしい遊びをしてみた (4)’

やーめったと。

成功はしなかったけど、「諦め」前に失敗作を記録しておこうと思って。

(4)で「これは意図と違うであろ?」を、考えられることは一応やってみた:

  1 #
  2 import sys
  3 import argparse
  4 import logging
  5 import json
  6 
  7 import numpy as np
  8 import av
  9 from PIL import Image, ImageOps, ImageColor
 10 
 11 def _conv_green(keyclr, src, threshold=16, sensitivity=1.5, replacewith="#333"):
 12     if keyclr[1] > keyclr[2]:  # green back
 13         c1i, c2i, c3i = 1, 0, 2
 14     else:  # blue back
 15         c1i, c2i, c3i = 2, 1, 2
 16     rw = ImageColor.getrgb(replacewith)
 17     rwsum = float(sum(rw))
 18     rw1r = 3 * rw[c1i] / rwsum
 19     rw2r = 3 * rw[c2i] / rwsum
 20     rw3r = 3 * rw[c3i] / rwsum
 21 
 22     arr = np.array(src)
 23     c1 = arr[:, :, c1i]
 24     c2 = arr[:, :, c2i]
 25     c3 = arr[:, :, c3i]
 26     cond = np.logical_and(c1 > threshold, np.logical_or(c1 > c2 * sensitivity, c1 > c3 * sensitivity))
 27     arr[:, :, c1i] = c1 * rw1r + (~cond) * c1
 28     arr[:, :, c2i] = cond * c1 * rw2r + (~cond) * c2
 29     arr[:, :, c3i] = cond * c1 * rw3r + (~cond) * c3
 30     return Image.fromarray(arr)
 31 
 32 if __name__ == '__main__':
 33     logging.basicConfig(stream=sys.stderr, level=logging.INFO)
 34     parser = argparse.ArgumentParser()
 35     parser.add_argument("background")
 36     parser.add_argument("greenback")
 37     parser.add_argument("outfile")
 38     parser.add_argument("--pick", type=str, default="[0, 0]")
 39     parser.add_argument("--atol", type=int, default=30)
 40     parser.add_argument("--bg-start", help="in secs", type=float, default=0)
 41     parser.add_argument("--gb-start", help="in secs", type=float, default=0)
 42     parser.add_argument("--gb-end", help="in secs", type=float, default=0)
 43     parser.add_argument("--audio-from-gb", action="store_true")
 44     parser.add_argument("--gb-base-alpha", type=int, default=0)
 45     args = parser.parse_args()
 46     pick = tuple(json.loads(args.pick))
 47     #
 48 
 49     icontbg = av.open(args.background)
 50     icontgb = av.open(args.greenback)
 51     ivstrbg = next(s for s in icontbg.streams if s.type == b'video')
 52     ivstrgb = next(s for s in icontgb.streams if s.type == b'video')
 53     iastrbg = next(s for s in icontbg.streams if s.type == b'audio')
 54     iastrgb = next(s for s in icontbg.streams if s.type == b'audio')
 55     ocont = av.open(args.outfile, "w")
 56     ovstr = ocont.add_stream(codec_name="h264", rate=ivstrbg.rate)
 57     oastr = ocont.add_stream(codec_name="aac", rate=iastrbg.rate)
 58     ovstr.width = ivstrbg.width
 59     ovstr.height = ivstrbg.height
 60     #
 61     if args.bg_start:
 62         bg_start_pts_v = int(args.bg_start / float(ivstrbg.time_base) + ivstrbg.start_time)
 63         bg_start_pts_a = int(args.bg_start / float(iastrbg.time_base) + iastrbg.start_time)
 64         ivstrbg.seek(bg_start_pts_v)
 65         iastrbg.seek(bg_start_pts_a)
 66     if args.gb_start:
 67         gb_start_pts_v = int(args.gb_start / float(ivstrgb.time_base) + ivstrgb.start_time)
 68         gb_start_pts_a = int(args.gb_start / float(iastrgb.time_base) + iastrgb.start_time)
 69         ivstrgb.seek(gb_start_pts_v)
 70         iastrgb.seek(gb_start_pts_a)
 71     if args.gb_end:
 72         gb_end_pts_v = int(args.gb_end / float(ivstrgb.time_base) + ivstrgb.start_time)
 73     else:
 74         gb_end_pts_v = 0
 75     #
 76 
 77     if not args.audio_from_gb:
 78         avbothpackets = icontbg.demux()
 79         vidonlypackets = icontgb.demux(ivstrgb)
 80     else:
 81         avbothpackets = icontgb.demux()
 82         vidonlypackets = icontbg.demux(ivstrbg)
 83     done = False
 84     while not done:
 85         try:
 86             avbothpacket = next(avbothpackets)
 87             if avbothpacket.stream.type == 'video':
 88                 if not args.audio_from_gb:
 89                     iframebgs = avbothpacket.decode()
 90                     iframegbs = next(vidonlypackets).decode()
 91                 else:
 92                     iframebgs = next(vidonlypackets).decode()
 93                     iframegbs = avbothpacket.decode()
 94                 for iframes in zip(iframebgs, iframegbs):
 95                     logging.info(iframes)
 96                     imgbg = iframes[0].to_image()
 97                     imggb = iframes[1].to_image().resize(imgbg.size)
 98                     keyclr = imggb.getpixel(pick)
 99                     r = np.array(imggb.getdata(0))
100                     g = np.array(imggb.getdata(1))
101                     b = np.array(imggb.getdata(2))
102                     a = np.ones(r.shape) * args.gb_base_alpha
103                     #
104                     a[np.logical_and(
105                             np.isclose(r, keyclr[0], atol=args.atol),
106                             np.isclose(g, keyclr[1], atol=args.atol),
107                             np.isclose(b, keyclr[2], atol=args.atol))] = 255
108                     alpha = Image.new("L", imggb.size)
109                     alpha.putdata(a.flatten())
110                     mask = imggb.copy()
111                     mask.putalpha(alpha)
112         
113                     dimg = Image.composite(imgbg, _conv_green(keyclr, imggb), mask)
114                     #dimg = Image.composite(
115                     #    imgbg,
116                     #    ImageOps.colorize(
117                     #        imggb.convert("L"),
118                     #        black="black", white="orange"),
119                     #    mask)
120 
121                     ofr = av.VideoFrame.from_image(dimg)
122                     for p in ovstr.encode(ofr):
123                         ocont.mux(p)
124                     if gb_end_pts_v and iframes[1].pts >= gb_end_pts_v:
125                         done = True
126             else:
127                 for iframe in avbothpacket.decode():
128                     logging.info(iframe)
129                     iframe.pts = None
130                     for p in oastr.encode(iframe):
131                         ocont.mux(p)
132         except StopIteration:
133             break
134 
135     ocont.close()

惜しいんだよな:

実際「それっぽければいい」んであれば、コメントアウトした「グレイスケールに変換してから colorize はまぁ悪くはなくて、活きてる方はパラメータによってはこの colorize と全然大差ないものが出来上がっちゃう。「緑を緑じゃなくしたい」だけなわきゃぁなくて、「赤は赤だろうよ」としたいから複雑なことを「やりかけた」わけだけど、ご覧の通り、緑の亡霊が…。

疲れたのでこれでやめる。(4)で宣言した通りで、こんなんワタシの本題じゃぁないんであって。