ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【アニメ感想】抱かれたい男1位に脅されています。 hug7「好きでいてもいいですか。」の演出について

www.nicovideo.jp

 今まで映画とかの感想は書いてきたのですが、今回は今現在放送中のアニメ「抱かれたい男1位に脅されています。」の7話の演出があまりに素晴らしかったので、それについての感想を書いていこうかと思います。本編はトップにリンクがあるのでそちらから確認してください。

 今回も今まで通り本編のキャプチャを交えつつ自分が感じたことをざっくばらんに書いていくので、ネタバレ前提で進めていきます。

 では始めます。


1:アバンの宇宙
 正直ここはよくわからない。今までの話数で出てきたカットか、もしくは次の話のどこかのカットに繋がるものだと思うのですが確認できていない状態。ただ何か劇的な変化を表現していそう。

2:見上げる鳥
f:id:rikoubou:20181120221505p:plain
 宇宙からのカットの後、過去の話に切り替わるのと同時に画面の色もセピア調に。運転するバイクから准太が見上げるのは1羽の鳥。まだ羽ばたくことのできていない自分自身か、もしくはすでに役者の高みにいる高人さんを表現していると思われる。そして准太はまだその鳥まで届いていない…。

3:信号演出
f:id:rikoubou:20181120222357p:plain
 続く信号のカット。セリフ通りの「順風満帆のオールグリーン」。一直線に並ぶだけの信号は画面としても理路整然としすぎていて、准太と同じくどこかつまらなさを感じるところがある。

4:高人さんとのファーストコンタクト
f:id:rikoubou:20181120222854p:plain
 准太が高人さんと初めて会うシーン。高人さんが画面上手に、准太が下手になっている。また二人の間を照明の足が分断するようにも配置されている。二人の間に分かり合えない壁が存在していることを意味している。以後も二人を分断するような道具の配置が何度も出てくる。
f:id:rikoubou:20181120223347p:plain
 また高人さんの背後に照明も配置されている。これは高人さんが文字通り光り輝くスターであることを視聴者に意識させる。
 上手にいる上に光まで背負っている、手の届かないほどの存在という強烈な印象を与えてくるシーンになっている。

5:三脚越しに視線を送る二人
f:id:rikoubou:20181120223726p:plainf:id:rikoubou:20181120223734p:plain
 三脚越しにお互いに視線を送っているという窮屈な画面。先ほども出た物理的な障壁という表現に加え、画面の窮屈さ、居心地の悪さも表現しているようにも思える。

6:キャラ同ポジのカット切り替え
f:id:rikoubou:20181120224158p:plainf:id:rikoubou:20181120224211p:plain
 痺れる! 最高にクール! まさに准太が高人さんに抱いているような感情を視聴者にもど直球で伝えてくる演出。文句なしにカッコいいと思える切り替え。

7:自販機前での二人の会話
f:id:rikoubou:20181120224641p:plain
 このコーヒーを吹き出すようなギャグっぽいシーンでも、高人さんが上手、准太が下手という関係は保たれている。
f:id:rikoubou:20181120224811p:plain
 そして詰め寄る場面でも、高人さんの背後には太陽からの光が注いでいる。

8:関係性の転換
f:id:rikoubou:20181120224959p:plain
 高人さんが准太の肩を叩くこの場面で初めて上手と下手が入れ替わる。話としてもこれをきっかけに准太に変化が起きるので、まさに転換となるシーン。
f:id:rikoubou:20181120225332p:plain
 この心象風景でも鳥が羽ばたいており、これからの変化を予感させる。

9:タブレットの意味
f:id:rikoubou:20181120225503p:plainf:id:rikoubou:20181120225516p:plain
 不安や戸惑いを紛らわすためにタブレットを乱暴に口へと放り込み、砕く。准太の中にある獣のような感情の一片も効果的に表現している。

10:ドラマの撮影
f:id:rikoubou:20181120231434p:plain
 准太に変化のきっかけはできたものの、ドラマにおいても下手のままでいる。まだ覚醒はしていない。

11:再度同じように視線を送る二人
f:id:rikoubou:20181120230224p:plainf:id:rikoubou:20181120230232p:plain
 5と同じような感じで視線を送る二人。二人の間にあるのは三脚ではなく、マイク?(照明?)になっている。二人の間の壁がより小さいものになっていることの表現だろう。

12:バスシーン
f:id:rikoubou:20181120230827p:plain
 hug3であったあのシーンと同じシチュエーション。視聴者はドキッとしてしまうが、同じシチュエーションでの二人の関係性の違いとむしろ准太が覚醒するきっかけを与えてくれる重要なシーンになっている。同じ場面を重要なところに持ってくるのが素晴らしい。

13:半覚醒後のドラマの撮影
f:id:rikoubou:20181120231544p:plain
 准太が上手に、高人さんが下手に配置されている。准太は先のバスのシーンから明らかに変わった存在として描かれている。
f:id:rikoubou:20181120231912p:plain
 抽象的なカットを挟みつつ濡れる事故もあって高人さんを襲おうとしてしまう准太。この抽象的な表現も心のごちゃごちゃを上手く表現できている。
f:id:rikoubou:20181120231934p:plain
 続くカットも准太の目は画面に入れず、影に注目させる不穏で恐ろしい印象を与える。准太の中にある獣が目覚めた瞬間を見事に描いている。

14:大量のタブレットとガラスに映る自分
f:id:rikoubou:20181120232702p:plain
 大量のタブレットを消費しており准太の心がかなり乱れていることが見て取れる。
f:id:rikoubou:20181120232831p:plain
 そしてガラスに映った自分を見つめる中で准太は自分の中にいる獣の存在を自覚する。

15:高人さん以外でも上手になる准太
f:id:rikoubou:20181120232951p:plain
 高人さん以外の相手にも、襲ってしまうかのような姿勢で体も大きく見せるようにして上手に位置している。獣を自覚し自分のものとした、まさに完全覚醒状態と言えるだろう。

16:覚醒後の二人の会話
f:id:rikoubou:20181120233245p:plain
 准太は役者として覚醒した。しかしまだ高人さんと准太の間にはスタンドの棒で仕切られた壁がある。
f:id:rikoubou:20181120233349p:plain
 高人さんに叩かれるシーン。ペットボトルから出た飛沫が准太の涙のようにも見える。准太は内心深いショックを受けていることが見て取れる表現。

17:エレベーター
f:id:rikoubou:20181120233512p:plain
 ここでも二人の間を邪魔するようにエレベーターの下へと降る矢印が壁として存在している。
f:id:rikoubou:20181120233654p:plain
 そして准太の「この人は俺との空間から早く出たいと思ってるんだろうな」のセリフの直後にピンポーンと正解の音をエレベーターが知らせる。

18:病院
f:id:rikoubou:20181120233756p:plain
 病院のベッドで寝ている高人さんにキスを迫ろうとする准太。しかし画面にはやはり二人の間を仕切るものが存在し、キスは未遂に終わってしまう。

19:タブレット消失からの羽ばたき
f:id:rikoubou:20181120233915p:plainf:id:rikoubou:20181120233929p:plain
 自分の気持ちに気がつき迷いのなくなった准太の手からタブレットは不要になっていた。そして鳥が羽ばたいて飛び上がるのと同じくして准太自身もより高みを目指すことを決意する。准太が羽を得た瞬間を描いたエモいシーン。

20:決意の別れ
f:id:rikoubou:20181120234213p:plain
 ドラマの撮影も終わりに近づいた時、准太は高人さんの前で手を伸ばそうとするがやめる。照明が今度は准太に当たるようになっていることと、のちに高人さんを見下ろす立場(抱かれたい男1位を取る)になることを暗示しているシーン。

21:色づきもう一度出会う
f:id:rikoubou:20181120234528p:plainf:id:rikoubou:20181120234538p:plainf:id:rikoubou:20181120234551p:plain
 セピア調だった画面が一度白くなり、そしてはっきりとした色へと変化する。退屈だと思っていた准太の日々が生まれ変わり、そしてまた高人さんに出会い色づいていくという演出。まさにやられたという感じで最高の盛り上がりを見せた後でラストのセリフが「高人さん」で締めるという大感動で終わる。

 ほぼ全部の流れを書いてしまった感じはありますが、本当に丁寧に演出されていて准太の気持ちと高人さんとの関係性がビシビシと伝わって来る作りでした。この記事執筆時点で2回視聴しているのですが見るたびに新しい発見がある素晴らしい回でした。お話としても素晴らしいです。

 以上が抱かれたい男1位に脅されています。のhug7を見て思ったことです。

 この作品のタイトルにあるように女性向けの作品で男性同士の恋愛をテーマにしていますが、その内容は純粋な人間関係の恋愛を描いています。現在放送中の「やがて君になる。」では女性同士の恋愛を描いているため、対比して鑑賞するとまた違いが楽しめるかと思います。個人的には君になるまでを描くのが「やがて君になる。」で、君になったあとを描いたのが「抱かれたい男1位に脅されています。」だと思い、両方楽しんでいます。

 やがて君になるの方も演出が凝っていて「どのシーンにも演出的意図があるのではないか?」と思いながら見れる素晴らしい作品です。

 未見の方は食わず嫌いをせずに見るのをオススメします!

 抱かれたい男1位に脅されています。はいいぞ!!

【python/OpenCV】画像を手書きっぽく加工するやつを作ってみる

 最近SNSなどでとある手書き風のアプリがリリースされちょっと話題になっています。

 iPhoneを持っていないのでどのようなアプリかはわかっていないのですが、結果の動画や画像を見る限りではOpenCVで再現できそうでした。そこでタイトルにあるように似たようなものを作れないかと思い、自分で色々調べて作ってみたのでその備忘録です。

 ちなみに自作したものは以下のような感じになります。

・加工前
f:id:rikoubou:20181115184750j:plain

・加工後
f:id:rikoubou:20181115184805p:plain

 完全に同じにはなりませんが、割と手書きな感じにはなっているかと思います。

 今回の画像加工の手順としては大体「グレースケール化→輪郭線抽出→影部分の抽出→輪郭線と影部分の合成」という順番でやっています。

 ではそれぞれについて説明していきます。読むのが面倒な場合は最後のソースコードのところまで飛ばしてください。


1:グレースケール化
 輪郭線を抽出する前段階として画像をグレースケール化します。輪郭線を抽出する前段階として一般的なものです。
 グレースケール化するには以下の「cv2.cvtColor」関数でできます。

import cv2

image = cv2.imread('hogehoge.jpg') # 画像取得
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレースケール


2:輪郭線抽出
 グレースケール化したものから輪郭線を抽出するのは「cv2.Canny」関数でできます。

outlineImage = 255 - cv2.Canny(grayImage, 110, 70)

 cv2.Cannyの第1引数がグレースケール化した画像です。第2引数と第3引数を変化させることで取得する線の調節ができます。
「255」という値から結果を引いているのは、cv2.Cannyの結果として線の部分が白、他が黒になっているためです。255から結果を引くことで色を反転させています。


3:影部分の抽出
 影部分の抽出は以下の手順でやっています。

grayImage = cv2.GaussianBlur(grayImage, (31, 31), 30) # ぼかす
ret, shadowImage = cv2.threshold(grayImage, 40, 220, cv2.THRESH_BINARY) # 閾値で2値化

 GaussianBlur関数でぼかし、そのあと2値化しています。これで影の部分が取得できます。


4:輪郭線と影部分の合成
 2つの画像ができたのであとは合成します。合成には「cv2.bitwise_and」関数を使います。

resultImg = cv2.bitwise_and(outlineImage, shadowImage)


5:ソースコード
 基本的には1〜4の手順で行い、適宜コントラストを減らしたりぼかしを加えたりなどして調節しています。
 今回作成したソースコードは以下の通りです。

 ・2018/11/19追記:画像サイズが奇数ピクセルだとエラーになっていたのでソースコードを修正しました。

・cv2Outline.py

import numpy as np
import cv2
import glob
from datetime import datetime
from time import sleep

RESIZE_SIZE = 4 # 画像を処理する時に使うサイズ(1/n)
CAMERA_SIZE = 2 # カメラ画像の縮小サイズ(1/n)

# カメラフラグでTrueにするとWebカメラの変換になる
CAMERA_FLG = False

IMG_FOLDER_PATH = "./img/*"     # 画像フォルダ
SAVE_FOLDER_PATH = "./result/"  # 出力保存フォルダ


# メイン関数
def main():
    print("--- start ---")

    # カメラの場合との場合分け
    if (CAMERA_FLG):
        # VideoCaptureのインスタンスを作成(引数でカメラを選択できる)
        cap = cv2.VideoCapture(0)
        changeCameraImage(cap)
        closeWindows(cap) # ウインドウを全て閉じる
    else:
        changeLoadImages(IMG_FOLDER_PATH, SAVE_FOLDER_PATH)

    print("--- end ---")


# カメラの映像を変換する関数
def changeCameraImage(cap):
    while True:
        ret, frame = cap.read() # 戻り値のframeがimg
        resultImg = changeImage(frame) # 画像変換

        # 結果をリサイズ
        fx = int(resultImg.shape[1]/CAMERA_SIZE)
        fy = int(resultImg.shape[0]/CAMERA_SIZE)
        resultImg = cv2.resize(resultImg, (fx, fy))

        # 文字を追加
        text = 'Exit is [Q] key'
        cv2.putText(resultImg, text, (0,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255,0), 3, cv2.LINE_AA)

        # 加工した画像を表示
        cv2.imshow('resultImg', resultImg)

        # キー入力を1ms待って、keyが「q」だったらbreak
        key = cv2.waitKey(1)&0xff
        if key == ord('q'):
            break


# 画像を読み込んで変換する関数
def changeLoadImages(imgFolderPath, saveFolderPath):
    images = glob.glob(imgFolderPath)

    for fname in images:
        frame = cv2.imread(fname) # 画像取得
        resultImg = changeImage(frame) # 画像変換

        # 画像保存
        saveImgByTime(saveFolderPath, resultImg)
        sleep(1)


# 画像を変換する関数
def changeImage(colorImg):
    gray = cv2.cvtColor(colorImg, cv2.COLOR_BGR2GRAY) # グレースケール
    gray = cv2.GaussianBlur(gray, (15, 15), 15) # ぼかす

    # 輪郭線処理
    img_diff = outine(gray) # 輪郭線抽出
    img_diff = cv2.GaussianBlur(img_diff, (11, 11), 8) # ぼかす
    ret, img_diff = cv2.threshold(img_diff, 170, 240, cv2.THRESH_BINARY) # 閾値で2値化

    # 影部分の処理
    gray = cv2.GaussianBlur(gray, (31, 31), 30) # ぼかす
    ret, gray = cv2.threshold(gray, 40, 220, cv2.THRESH_BINARY) # 閾値で2値化
    gray = lowContrast(gray) # コントラストを落とす
    
    # 輪郭線と影部分の画像を合成
    resultImg = cv2.bitwise_and(img_diff, gray)

    return resultImg


# 画像の輪郭線を抽出する関数
def outine(grayImg):
    # リサイズ
    fx = int(grayImg.shape[1]/RESIZE_SIZE)
    fy = int(grayImg.shape[0]/RESIZE_SIZE)

    grayChangeImg = cv2.resize(grayImg, (fx, fy))
    # 輪郭線抽出
    result = 255 - cv2.Canny(grayChangeImg, 110, 70)
    # リサイズして元に戻す
    result = cv2.resize(result, (grayImg.shape[1], grayImg.shape[0]))
    return result


# コントラストを落とす関数
def lowContrast(img):
    # ルックアップテーブルの生成
    min_table = 50
    max_table = 230
    diff_table = max_table - min_table
    look_up_table = np.arange(256, dtype = 'uint8' )
 
    for i in range(0, 255):
        look_up_table[i] = min_table + i * (diff_table) / 255
 
    # コントラストを低減
    result = cv2.LUT(img, look_up_table)
    return result


# 画像を時刻で保存する関数
def saveImgByTime(dirPath, img):
    # 時刻を取得
    date = datetime.now().strftime("%Y%m%d_%H%M%S")
    path = dirPath + date + ".png"
    cv2.imwrite(path, img) # ファイル保存


# キャプチャをリリースして、ウィンドウをすべて閉じる関数
def closeWindows(cap):
    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

 少し説明すると「cv2Outline.py」として保存したファイルと同じ場所に「img」、「result」のフォルダをそれぞれ作成します。そして「img」フォルダに加工前の画像を入れて「python3 cv2Outline.py」を実行すると「result」フォルダに結果が保存されます。

 また以下の部分の「False」を「True」に書き換えて実行するとWebカメラの表示に切り替わります。

# カメラフラグでTrueにするとWebカメラの変換になる
CAMERA_FLG = False

 2018/11/19追記:また画像が小さいといい感じに輪郭線の抽出ができていないようだったため、画像サイズに合わせてある程度計算で適切に処理するようなバージョンも作りました。

・alt_cv2Outline.py

import numpy as np
import cv2
import glob
from datetime import datetime
from time import sleep

CAMERA_SIZE = 2 # カメラ画像の縮小サイズ(1/n)

BLUR_VALUE = 25000 # ブラーをかけるための定数
PIXEL_VALUE = 300000 # 拡大/縮小の基準となるピクセル

# カメラフラグでTrueにするとWebカメラの変換になる
CAMERA_FLG = False

IMG_FOLDER_PATH = "./img/*"     # 画像フォルダ
SAVE_FOLDER_PATH = "./result/"  # 出力保存フォルダ

# メイン関数
def main():
    print("--- start ---")

    # カメラの場合との場合分け
    if (CAMERA_FLG):
        # VideoCaptureのインスタンスを作成(引数でカメラを選択できる)
        cap = cv2.VideoCapture(0)
        changeCameraImage(cap)
        closeWindows(cap) # ウインドウを全て閉じる
    else:
        changeLoadImages(IMG_FOLDER_PATH, SAVE_FOLDER_PATH)

    print("--- end ---")


# カメラの映像を変換する関数
def changeCameraImage(cap):
    while True:
        ret, frame = cap.read() # 戻り値のframeがimg
        resultImg = changeImage(frame) # 画像変換

        # 結果をリサイズ
        fx = int(resultImg.shape[1]/CAMERA_SIZE)
        fy = int(resultImg.shape[0]/CAMERA_SIZE)
        resultImg = cv2.resize(resultImg, (fx, fy))

        # 文字を追加
        text = 'Exit is [Q] key'
        cv2.putText(resultImg, text, (0,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255,0), 3, cv2.LINE_AA)

        # 加工した画像を表示
        cv2.imshow('resultImg', resultImg)

        # キー入力を1ms待って、keyが「q」だったらbreak
        key = cv2.waitKey(1)&0xff
        if key == ord('q'):
            break


# 画像を読み込んで変換する関数
def changeLoadImages(imgFolderPath, saveFolderPath):
    images = glob.glob(imgFolderPath)

    for fname in images:
        frame = cv2.imread(fname) # 画像取得
        resultImg = changeImage(frame) # 画像変換

        # 画像保存
        saveImgByTime(saveFolderPath, resultImg)
        sleep(1)


# 画像を変換する関数
def changeImage(colorImg):
    gray = cv2.cvtColor(colorImg, cv2.COLOR_BGR2GRAY) # グレースケール

    # ピクセル数からぼかす値を計算
    allPixel = colorImg.shape[1] * colorImg.shape[0]
    bokashi = calcBlurValue(allPixel)
    gray = cv2.GaussianBlur(gray, (bokashi, bokashi), bokashi) # ぼかす

    # 輪郭線処理
    img_diff = outine(gray, allPixel) # 輪郭線抽出

    # 輪郭線用のぼかし計算
    bokashiOutline = bokashi
    if bokashi > 4:
        bokashiOutline = bokashi - 4

    img_diff = cv2.GaussianBlur(img_diff, (bokashiOutline, bokashiOutline), bokashiOutline) # ぼかす
    ret, img_diff = cv2.threshold(img_diff, 170, 240, cv2.THRESH_BINARY) # 閾値で2値化

    # 影部分の処理
    gray = cv2.GaussianBlur(gray, (bokashi, bokashi), bokashi) # ぼかす
    ret, gray = cv2.threshold(gray, 40, 220, cv2.THRESH_BINARY) # 閾値で2値化
    gray = lowContrast(gray) # コントラストを落とす
    
    # 輪郭線と影部分の画像を合成
    resultImg = cv2.bitwise_and(img_diff, gray)

    return resultImg


# ブラーをかける値を計算する関数
def calcBlurValue(allPixel):
    result = int(np.sqrt(allPixel/BLUR_VALUE))
    if (result%2 == 0):
        result = result + 1
    return result


# 画像の輪郭線を抽出する関数
def outine(grayImg, allPixel):
    # リサイズ
    z =  np.sqrt(PIXEL_VALUE / (grayImg.shape[1] * grayImg.shape[0]))
    if (z > 1):
        z = 1

    fx = int(grayImg.shape[1] * z)
    fy = int(grayImg.shape[0] * z)

    grayChangeImg = cv2.resize(grayImg, (fx, fy))
    # 輪郭線抽出
    result = 255 - cv2.Canny(grayChangeImg, 100, 50)
    # リサイズして元に戻す
    result = cv2.resize(result, (grayImg.shape[1], grayImg.shape[0]))
    return result


# コントラストを落とす関数
def lowContrast(img):
    # ルックアップテーブルの生成
    min_table = 50
    max_table = 230
    diff_table = max_table - min_table
    look_up_table = np.arange(256, dtype = 'uint8' )
 
    for i in range(0, 255):
        look_up_table[i] = min_table + i * (diff_table) / 255
 
    # コントラストを低減
    result = cv2.LUT(img, look_up_table)
    return result


# 画像を時刻で保存する関数
def saveImgByTime(dirPath, img):
    # 時刻を取得
    date = datetime.now().strftime("%Y%m%d_%H%M%S")
    path = dirPath + date + ".png"
    cv2.imwrite(path, img) # ファイル保存
    print("saved: " + date + ".png")


# キャプチャをリリースして、ウィンドウをすべて閉じる関数
def closeWindows(cap):
    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

 画像の大きさに合わせてそれぞれのソースコードを使い分ければいいかなと思っています。(どうしても画像のサイズや単純なものが写っているかどうかに影響してくるので…)


 以上が今回作ってみたものです。OpenCVpythonがあれば手軽に色々な加工ができて面白いです。

 また今回作ったソースコードも一応公開しておきます。


・参考資料

【python/OpenCV】OpenCVでWebカメラの画像を取得する

 OpenCVを入れたのでちょっとWebカメラで遊んでみるというタイトル通りの内容です。

 基本的には参考にした内容ほぼ丸パクリです。


1:OpenCVWebカメラの画像を取得する
 以下の記述でできます。

import cv2 # OpenCV のインポート

# VideoCaptureのインスタンスを作成(引数の番号でカメラを選択できる)
cap = cv2.VideoCapture(0)

# VideoCaptureから1フレーム読み込む
ret, frame = cap.read() # 戻り値のframeが画像

「cap = cv2.VideoCapture(0)」で接続しているWebカメラインスタンスを作成しています。ノートPCなどに内臓されているカメラの場合は、引数に「0」を指定してあげれば良いです。
「ret, frame = cap.read()」でカメラの画像を1フレーム読み込みます。この戻り値のframeが読み込んだ画像になります。


2:サンプルコード
 1の内容でWebカメラの画像が取得できました。これを応用してカメラの画像に現在時刻を表示させるサンプルを作りました。

 そのサンプルが以下になります。

from datetime import datetime # 時刻関係のライブラリ
import cv2 # OpenCV のインポート

# VideoCaptureのインスタンスを作成(引数でカメラを選択できる)
cap = cv2.VideoCapture(0)

while True:
    # VideoCaptureから1フレーム読み込む
    ret, frame = cap.read() # 戻り値のframeがimg

    # 1/4にリサイズ
    frame = cv2.resize(frame, (int(frame.shape[1]/4), int(frame.shape[0]/4)))
    # 加工なし画像を表示する
    # cv2.imshow('Raw Frame', frame)

    # 現在時刻の文字列を画像に追加
    date = datetime.now().strftime("%H:%M.%S")
    edframe = frame
    cv2.putText(edframe, date, (0,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255,0), 3, cv2.LINE_AA)

    # 加工した画像を表示
    cv2.imshow('Edited Frame', edframe)

    # キー入力を1ms待って、keyが「q」だったらbreak
    key = cv2.waitKey(1)&0xff
    if key == ord('q'):
        break

# キャプチャをリリースして、ウィンドウをすべて閉じる
cap.release()
cv2.destroyAllWindows()

 このサンプルを実行すると、Webカメラの元画像を1/4した大きさのものに現在時刻が左上に表示されます。
 終了させる時は「q」キーを押すと終了します。


 以上がWebカメラの画像を取得する方法です。割と簡単に実装できたのでOpenCVを使ってもっと色々なことをやっていきたいです。


・参考資料

【Blender/OpenCV】BlenderでOpenCVを使えるようにする

 今回はタイトルにあるようにBlenderOpenCVを使えるようにする方法の備忘録です。
 別にBlenderOpenCVを使う必要はないかもしれませんが、調べてやってみたらできたので残しておきます。

 以前の記事でやったOpenCVのインストールまで終わっているという前提で進めていきます。
rikoubou.hatenablog.com

 では始めます。


1:OpenCVのインストール先を確認する
 ターミナルを起動し「python」と入力してpythonを立ち上げます。
 pythonを立ち上げたら以下のコマンドを入力してOpenCVのインストールパスを確認します。

import cv2
cv2

 実行すると以下のようになるので、表示される「.../cv2/」までのパス(赤線部分)をコピーしておきます。
f:id:rikoubou:20181106134550p:plain


2:Blenderを起動させてOpenCVを読み込ませる
 1でOpenCVのパスを確認できたらBlenderを起動させ、Scripting画面に切り替えます。
f:id:rikoubou:20181106134848p:plain
f:id:rikoubou:20181106134924p:plain

 下半分の黒い画面がpythonの入力ができるコンソール画面になっているので、ここにpythonのコードを入力してOpenCVを使えるようにします。

 Blenderでは外部のライブラリの読み込みは自動で行ってくれないので、手動で設定してやる必要があります。
 pythonでの外部ライブラリの読み込みは以下のように記述します。

import sys
sys.path.append("[ライブラリのパス]")

 なのでOpenCVを使うには上記のコードをBlenderのコンソール画面に入力していきます。

 一行ずつ入力して行きエラーが発生しなければ以下のように「import cv2」をすることでBlenderOpenCVを扱えるようになります。
f:id:rikoubou:20181106140151p:plain


3:「import cv2」の実行でエラーが出る時の対処法
 sys.path.appendをした後の「import cv2」でエラーが出ることがあります。

  • パスが間違っている場合

 以下のようなエラーが出る場合があります。

import cv2

Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
ModuleNotFoundError: No module named 'cv2'

 この場合はライブラリまでのパスが間違っている可能性が高いので、もう一度ライブラリのパスを確認してください。

  • numpyのバージョンが合っていない場合

 以下のようなエラーが出る場合があります。

import cv2

RuntimeError: module compiled against api version 0xc but this version of numpy is 0xa
(色々と続く...)

 この場合はOpenCVのnumpyとBlenderのnumpyのバージョンが合ってない時に起こるエラーです。
 対処するには以下のコマンドで「ターミナルから起動するpythonでのnumpyのバージョン」と「Blenderでのnumpyのバージョン」を確認してください。

import numpy
numpy.__version__

 ターミナルから起動するpythonBlenderでバージョンをそれぞれ確認します。
f:id:rikoubou:20181106141622p:plainf:id:rikoubou:20181106141543p:plain

 ここの例ではバージョンが1.15系として合っていますが、違っている場合は以下の方法で両方のnumpyを最新にします。

・ターミナルから起動するnumpyのアップグレード
 pipでインストールしているので、以下のコマンドを入力してnumpyをアップグレードさせます。

$ pip install numpy --upgrade

Blenderのnumpyアップグレード
 以下のリンクにある「Official」のマークが付いている最近のBlenderをダウンロードしてインストールします。

 両方とも最新にするとnumpyのバージョンが合うようになるはずなので、もう一度同じようにライブラリをappendさせてから「import cv2」をするとエラーが解消されます。


 以上がBlenderOpenCVを使えるようにする方法です。
 まだOpenCV自体何もわかっていないので、組み合わせて何か面白いものを作れるようになりたいです。


・参考資料

【python/OpenCV】インストールしたpythonとOpenCVのパスを確認する

 以前の記事でpythonOpenCVのインストールをしました。

 ちょっと気になってそれぞれどこにインストールされてるのか知りたかったのですが、ぱっと見よくわかりませんでした。
 調べたところ「pythonを開いてコマンドを叩けば良い」ということがわかったので、そのメモです。


・ライブラリのインストール場所を確認する方法
 基本的には以下の方法で確認できます。

import [ライブラリ]
print([ライブラリ].path)

 pythonのインストール先を確認するのは以下のコマンドでできます。

import sys
print(sys.path)

 出力結果は以下のようになります。
f:id:rikoubou:20181105173950p:plain

 OpenCVのインストール先を確認するのは以下のコマンドでできます。

import cv2
print(cv2)

 出力結果は以下のようになります。
f:id:rikoubou:20181105174114p:plain


 以上がパスの確認方法です。

 パスが必要になる時が稀によくあるので、その時のためのメモです…。


・参考資料

【python/OpenCV】MacOSにpythonとOpenCVの環境を構築する

 pythonOpenCV使ってみるかー、と思い立って環境構築しようとしたのですが、自分が使っているMacpythonの環境がよくわからなくなってしまっていたので、一度まっさらにしてから入れ直すということをやりました。

 色々と戸惑ってしまった部分も多かったので備忘録として残そうと思った次第です。


1:HomeBrewをインストールする
 HomeBrewをインストールしていない場合はHomeBrewをインストールします。

 ターミナルを立ち上げて以下のコマンドを実行すればインストールされます。

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"


2:pyenvをインストールする
 pyenvは使用するpythonのバージョンを切り替えたり管理したりするためのものです。

 HomeBrewをインストールできたら、以下のコマンドを実行してpyenvをインストールします。

$ brew install pyenv

 この時に以下のような権限エラーが出る場合があります。(自分の場合は出ました)

$ brew install pyenv

Updating Homebrew...
Error: The following directories are not writable by your user:
/usr/local/share/man/man5
/usr/local/share/man/man7

You should change the ownership of these directories to your user.
  sudo chown -R $(whoami) /usr/local/share/man/man5 /usr/local/share/man/man7

 このようなエラーが出た場合はエラーの記述にある通り以下のコマンドを実行して権限を変更します。

$ sudo chown -R [ユーザ名] /usr/local/share/man/man5 /usr/local/share/man/man7

 ユーザ名のところに権限を与えたい名前を入れて実行すると権限を変更でき、その後に再度「brew install pyenv」コマンドを実行するとエラーなく実行することができます。


3:インストールできる一覧を確認する
 以下のコマンドを実行するとインストールできる一覧が表示されます。

$ pyenv install --list

 何も書かれていない「3.7.0」などがpythonのバージョンです。他にも「anaconda3-5.2.0」などanacondaも選択できます。


4:バージョンを指定してpythonをインストールする
 一覧の中にあるものからインストールしたいものを選択してインストールします。(基本的には一番最新のバージョンで良いかと思います)

 以下のコマンドでインストールできます。

$ pyenv install [バージョン番号]

 python3.7.0をインストールしたい場合は以下のように記述します。

$ pyenv install 3.7.0


5:pythonのバージョンを切り替える
 pythonをインストールできたら、以下のコマンドを実行してインストールされている一覧を確認します。

$ pyenv versions

 これを実行すると以下のようにインストールされている一覧と現在選択中のバージョンが表示されます。
f:id:rikoubou:20181029163649p:plain

「*」のマークがついているのが、現在選択されているバージョンです。

 pythonのバージョンを切り替えるには以下のコマンドを実行します。

$ pyenv global [バージョン番号]

 python 3.7.0に切り替えたい場合は以下のようにします。

$ pyenv global 3.7.0

 このコマンドを実行した後にもう一度「pyenv versions」を実行すると、以下のように使用するpythonが切り替わります。
f:id:rikoubou:20181029164059p:plain

 バージョンが切り替わったかの確認として以下のコマンドを実行します。

$ python -V

 すると以下のようにpythonのバージョンを確認できます。
f:id:rikoubou:20181029164146p:plain


6:環境変数の設定
 以下のコマンドで「.bash_profile」をviエディタを開きます。

$ vi ~/.bash_profile

「i」キーを押して「insertモード」に切り替えてから、以下の一行をコピペして追記します。

export PATH="$HOME/.pyenv/shims:$PATH"

 追記が終了したら「ESC」キーを押して「コマンドモード」に切り替えてから、以下のコマンドを入力して上書き保存します。

:wq

 ファイルの上書き保存が終了したら、以下のコマンドを実行してファイルを再読み込みします。

$ source ~/.bash_profile


7:pipを使ってOpenCVをインストールする
 次にOpenCVをインストールします。pyenvを使ってpythonをインストールするとpipというのも入っているので、pipを使ってOpenCVを入れます。

 OpenCVを入れるには以下のコマンドを実行するだけです。

$ pip install opencv-python

 また商用には非推奨(特許関連のものも含まれているらしい)ですが、以下のコマンドを実行すると通常のOpenCVよりもより機能が含まれているものをインストールできます。

$ pip install opencv-contrib-python

 上記の2つのコマンドは競合してしまうようなので、どちらか一方をインストールした後に変更したい場合は以下のように一度アンインストールコマンドを実行してから入れ直す必要があります。

$ pip uninstall opencv-python
$ pip uninstall opencv-contrib-python


8:pythonOpenCVが使えるかの確認
 OpenCVのインストールができたら、pythonからOpenCVを使えるかの確認をします。

 ターミナルで「python」と入力してpythonを立ち上げて「import cv2」と入力します。これでエラーが出なければ、OpenCVを使えるようになっています。
f:id:rikoubou:20181029165808p:plain

 確認できたら「Ctrl+D」キーでpythonを終了させます。
 

 以上がMacOSpythonのインストールとpythonOpenCVを使えるようにするまでの手順です。

 ネットで探すと色々方法があって戸惑いましたが、一応上記のやり方でできました。
 これで画像処理とかをやっていきたいですね。


・参考資料

【Blender/イベント】BlendxJP3に参加してきました

techplay.jp

 2018/10/27(土)に渋谷にて行われた「BlendxJP3」に参加してきました。

 会場での写真は全く撮影してなかったので掲載することはできませんが、登壇者参加者含めて170名ほどの人が集まった熱気のあるイベントで非常に楽しかったです。

 発表内容も「Blenderpythonアドオンを作ろう!」というところから始まり、VRアドベンチャーの「東京クロノス」に関するものやBlender2.8のレンダリング、2DアニメーションによるBlender使用事例の紹介などなど盛りだくさんでした。

 終了後の懇親会でも他業種の様々な方とお話できて非常に有意義でした。お話させていただいた方々ありがとうございました。

 次があればまた参加したいと思います。

  Blenderはいいぞ!!!

 ちなみに当日のTwitterの内容がまとめられているようなので、そちらもどうぞ。
togetter.com