React: 再生ボタンを押すと音楽が流れるようにしたい

インストール

yarn add @material-ui/core
yarn add @material-ui/icons
yarn add react-sound

使用例

import React, { Component } from 'react';
import IconButton from '@material-ui/core/IconButton';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import Sound from 'react-sound';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      status: Sound.status.STOPPED,
    }
  }
  render() {
    return (
      <div>
        <Sound url="[サウンドファイル]" playStatus={this.state.status} />
        <IconButton aria-label="Play/pause" onClick={()=>{this.setState({ status: Sound.status.PLAYING })}}>
	  <PlayArrowIcon />
        </IconButton>
      </div>
    );
  }
}

こんな感じでしょうか。

Python: OpenCVで動画に字幕をつける

ややタイトル詐欺ですが、OpenCVで動画を読み込んで、画像処理ライブラリPillow(PIL)で字幕をつけます。OpenCVのputText()で字幕をつけることもできますが、日本語は文字化けしてしまうようです。

インストール

sudo yum install -y python3
sudo yum install ipa-gothic-fonts 
sudo pip3 install opencv-python
sudo pip3 install pillow

字幕をつける

img.MOVを読み込んでoutput.m4vに字幕をつけて出力します。

import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image

cap = cv2.VideoCapture('./img.MOV')
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter('output.m4v', fourcc, fps, (width, height))

while True:
    ret, frame = cap.read()
    if ret:
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pil_image = Image.fromarray(frame_rgb)
        draw = ImageDraw.Draw(pil_image)
        font = ImageFont.truetype('/usr/share/fonts/ipa-gothic/ipag.ttf', 50)
        draw.text((50, 300), '国会議事堂', font=font)
        rgb_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
        out.write(rgb_image)
    else:
        break

cap.release()
out.release()

色の表現方法が、OpenCVはBGRであるのに対して、PillowはRGBですので、その変換が必要になっています。
結果は以下の通りです。


Python: OpenCVを使用して、アメ横を行き交う人々のエッジを検出する

何の役に立つかわかりませんが、アメ横を行き交う人々のエッジを検出しました。

import cv2
import numpy as	np

cap = cv2.VideoCapture('./img.MOV')
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter('output.m4v', fourcc, fps, (height, width))                                                                     

while True:
    ret, frame = cap.read()
    if ret:                                                                                                             
        frame270 = np.rot90(frame, 3)                                                 
        dst = cv2.Canny(frame270, 50, 200)
        bgr = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
        out.write(bgr)
    else:
        break

cap.release()
out.release()

「bgr = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)」がポイントです。これをしないと正常に動画を出力できません。

結果は以下の通りです。

cv2.bitwise_not(dst)として、白黒反転させた方がいい感じです。

Python: OpenCVを使用して、アメ横を行き交う人々の顔にモザイクをかける

今までやってきたことを応用して、アメ横を行き交う人々の顔にモザイクをかけていきます。

  • Python: OpenCVを使用して動画を読み込んで、何もせず書き込む1
  • Python: OpenCVを使用して動画を読み込んで、何もせず書き込む2
  • Python: OpenCVを使用して顔検出をする
  • すべてのフレームについて、それぞれ顔検出をし、モザイクをかけていきます。

    import cv2
    import numpy as np
    
    cascade = cv2.CascadeClassifier('/usr/local/lib64/python3.7/site-packages/cv2/data/haarcascade_frontalface_alt2.xml')
    
    def mosaic(img):
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        rects = cascade.detectMultiScale(gray, 1.1, 1, 0, (5,5))
        if len(rects) > 0:
            for rect in rects:
                x, y, w, h = rect
                face = img[y:y+h, x:x+w]
                dst = cv2.GaussianBlur(face, (25, 25), 10)
                img[y:y+h, x:x+w] = dst
        return img
                
    cap = cv2.VideoCapture('./img.MOV')
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter('output.m4v', fourcc, fps, (height, width))
    
    while True:
        ret, frame = cap.read()
        if ret:
            frame270 = np.rot90(frame, 3)
            out.write(mosaic(frame270))
        else:
            break
    
    cap.release()
    out.release()
    

    実行してみましたが、角度によって顔として検出されない場合があり、モザイクがチカチカしてしまいます。そこで、少し強引な手法ですが、顔検出した位置について、その後の15フレームについてモザイクをかけるようにしました。

    import cv2
    import numpy as np
    
    cascade = cv2.CascadeClassifier('/usr/local/lib64/python3.7/site-packages/cv2/data/haarcascade_frontalface_alt2.xml')
    
    def mosaic(img, rects_list):
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        rects = cascade.detectMultiScale(gray, 1.1, 1, 0, (5,5))
        rects_list.append(rects)
        for rects in rects_list:
            for rect in rects:
                x, y, w, h = rect
                face = img[y:y+h, x:x+w]
                dst = cv2.GaussianBlur(face, (25, 25), 10)
                img[y:y+h, x:x+w] = dst
        return img, rects
                
    cap = cv2.VideoCapture('./img.MOV')
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter('output.m4v', fourcc, fps, (height, width))
    
    rects_list = []
    while True:
        ret, frame = cap.read()
        if ret:
            frame270 = np.rot90(frame, 3)
            mosaiced_frame, rects = mosaic(frame270, rects_list[-15:])
            rects_list.append(rects)
            out.write(mosaiced_frame)
        else:
            break
    
    cap.release()
    out.release()
    

    結果は以下の通りです。


    無事モザイクをかけることができました。

    Python: OpenCVを使用して動画を読み込んで、何もせず書き込む2

    Python: OpenCVを使用して動画を読み込んで、何もせず書き込むの結果、動画の縦横が逆になってしまったので、修正していきます。

    import cv2
    import numpy as	np
    
    cap = cv2.VideoCapture('./img.MOV')
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter('output.m4v', fourcc, fps, (height, width))
    
    while True:
        ret, frame = cap.read()
        if ret:
            out.write(np.rot90(frame, 3))
        else:
            break
    
    cap.release()
    out.release()
    

    (width, height)を(height, width)に変更しました。
    また、書き込む際に、out.write(np.rot90(frame, 3))として270度回転しました。
    これで、もとの動画とまったく同じ動画が出力できました。

    Python: OpenCVを使用して動画を読み込んで、何もせず書き込む

    インストール

    Python3とOpenCVをインストールします。

    sudo yum install -y python3
    sudo pip3 install opencv-python
    

    動画を読み込んで、何もせず書き込む

    import cv2
    
    cap = cv2.VideoCapture('./img.MOV')
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter('output.m4v', fourcc, fps, (width, height))
    
    while True:
        ret, frame = cap.read()
        if ret:
            out.write(frame)
        else:
            break
    
    cap.release()
    out.release()
    

    とりあえず、これでうまくいきました。縦横が逆になっていますが…

    Python: AWS Lambdaでメールを送信する

    「AWS Lambda」->「関数の作成」->「一から作成」で関数を作成する。

    import boto3
    
    def send_email(source, to, subject, body):
        client = boto3.client('ses')
        response = client.send_email(
        Destination={
    	'ToAddresses': [
                to
    	],
        },
        Message={
    	'Body': {
                'Text': {
    		'Charset': 'UTF-8',
    		'Data': body,
                },
    	},
            'Subject': {
                'Charset': 'UTF-8',
                'Data': subject,
    	},
        },
        Source=source
        )
        return response
    
    def lambda_handler(event, context):
        source = '[メールアドレス]'
        to = '[メールアドレス]'
        subject = 'TEST'
        body = 'TEST\nTEST'
        r = send_email(source, to, subject, body)
        return r
    

    IAMロールの設定を正しく行っていないと、以下のようなエラーが出力される。

    {
      "errorMessage": "An error occurred (AccessDenied) when calling the SendEmail operation: User `XXXXXXXX’ is not authorized to perform `ses:SendEmail' on resource `XXXXXXXX’”,
      "errorType": "ClientError",
      "stackTrace": [
        [
          "/var/task/lambda_function.py",
          32,
          "lambda_handler",
          "r = send_email(source, to, subject, body)"
        ],
        [
          "/var/task/lambda_function.py",
          23,
          "send_email",
          "Source=source"
        ],
        [
          "/var/runtime/botocore/client.py",
          314,
          "_api_call",
          "return self._make_api_call(operation_name, kwargs)"
        ],
        [
          "/var/runtime/botocore/client.py",
          612,
          "_make_api_call",
          "raise error_class(parsed_response, operation_name)"
        ]
      ]
    }
    

    IAMロールにAmazonSESFullAccessポリシーをアタッチすることで解決。

    Python: Boto3を使用してメールを送信する

    boto3のインストール

    pip install boto3
    

    メールの送信

    import boto3
    
    def send_email(source, to, subject, body):
        client = boto3.client('ses')
        response = client.send_email(
        Destination={
    	'ToAddresses': [
                to
    	],
        },
        Message={
    	'Body': {
                'Text': {
    		'Charset': 'UTF-8',
    		'Data': body,
                },
    	},
            'Subject': {
                'Charset': 'UTF-8',
                'Data': subject,
    	},
        },
        Source=source
        )
        return response
    
    if __name__ == '__main__':
        source = '[送信元]'
        to = '[送信先]'
        subject = '[タイトル]'
        body = '[本文]'
        r = send_email(source, to, subject, body)
        print(r)
    

    以下のようなエラーが出力される場合には、「Simple Email Service」->「Email Addresses」でメールアドレスを承認しておく。

    Traceback (most recent call last):
      File "test.py", line 32, in 
        r = send_email(source, to, subject, body)
      File "[ファイル].py", line 23, in send_email
        Source=source
      File "/usr/local/lib/python3.7/site-packages/botocore/client.py", line 357, in _api_call
        return self._make_api_call(operation_name, kwargs)
      File "/usr/local/lib/python3.7/site-packages/botocore/client.py", line 661, in _make_api_call
        raise error_class(parsed_response, operation_name)
    botocore.errorfactory.MessageRejected: An error occurred (MessageRejected) when calling the SendEmail operation: Email address is not verified. The following identities failed the check in region US-EAST-1: [メールアドレス]
    

    React: react-helmetを使用してtitleタグを書き換える

    インストール

    yarn add react-helmet
    

    titleタグを書き換える

    import React, { Component } from 'react';
    import { Helmet } from 'react-helmet';
    
    class App extends Component {
      render() {
        return (
          <div>
            <Helmet title="[タイトル]" />
          </div>
        );
      }
    }
    
    export default App;
    

    もしくは、

    import React, { Component } from 'react';
    import { Helmet } from 'react-helmet';
    
    class App extends Component {
      render() {
        return (
          <div>
            <Helmet>
              <title>
                [タイトル]
              </title>
          </Helmet>
          </div>
        );
      }
    }
    
    export default App;