Python: boto3を使用してDynamoDBを操作する

itemの追加

import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('[テーブル名]')
table.put_item(Item={'[キー]': '[値]'})

itemの取得

import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('[テーブル名]')
item = table.get_item(Key={'[キー]': '[値]'})

itemの削除

import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('[テーブル名]')
table.delete_item(Key={'[キー]': '[値]'})

Python: リストからランダムに要素を選択する

ランダムに要素を一つ選択する

mylist = [1, 2, 3, 4, 5]
print(random.choice(mylist))
#2

ランダムに要素を複数選択する(重複なし)

mylist = [1, 2, 3, 4, 5]
print(random.sample(mylist, 2))
#[4, 5]

ランダムに要素を複数選択する(重複あり)

mylist = [1, 2, 3, 4, 5]
print(random.choices(mylist, k=3))
#[1, 1, 5]

choicesはPython3.6から追加された機能なので、それ以前のバージョンで実行すると以下のようなエラーが出力されます。

Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'module' object has no attribute 'choices'

Python: 「<」と「>」を参照文字に変換する

最近、Reactの記事を書くことが多くなってきたので、「<」と「>」を参照文字に変換するスクリプトを作成しました。標準入力からコードを読み込んで、「<」を「&lt;」に、「>」を「&gt;」に変換するだけです。「exit」と打つと、入力を終了して、変換結果を出力します。

code = []
while True:
    line = input()
    if	line == 'exit':
        break
    code.append(line)
code_replaced = '\n'.join(code).replace('<', '&lt;').replace('>', '&gt;')
print(code_replaced)

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: [メールアドレス]