質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

blenderのpythonでwebカメラが読み込みたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 486

okadanaoki

score 5

環境
Blender2.8
Python 3.7.3
macOS High Sierra 10.13.6

上記の環境の元、https://youtu.be/O7nNO3FLkLU こちらのリンク先の動画にあるように、blenderのpythonからopencvを使ってwebカメラを読み込みたいです。

しかし以下のようなエラーが出て、カメラを読み込むことができません。

 [ERROR:0] VIDEOIO(AVFOUNDATION): raised unknown C++ exception! Traceback (most recent call last): File "/Users/okadanaoki/Desktop/mocapdata/Vincent.blend/Text.001", line 83, in modal cv2.error: OpenCV(4.1.0) /Users/travis/build/skvark/opencv-python/opencv/modules/imgproc/src/color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cvtColor' location: <unknown location>:-1 location: <unknown location>:-1

いかに全文を乗せておきます。

import bpy
from imutils import face_utils
import dlib
import cv2
import time
import numpy
from bpy.props import FloatProperty

class OpenCVAnimOperator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.opencv_operator"
bl_label = "OpenCV Animation Operator"

# p = our pre-treined model directory
p = "/Users/okadanaoki/Desktop/mocapdata/shape_predictor_68_face_landmarks.dat" # macOS
#p = "/home/jason/Downloads/shape_predictor_68_face_landmarks.dat" # linux
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(p)

_timer = None
_cap  = None

width = 800
height = 600

stop :bpy.props.BoolProperty()

# 3D model points.    
model_points = numpy.array([
                            (0.0, 0.0, 0.0),             # Nose tip
                            (0.0, -330.0, -65.0),        # Chin
                            (-225.0, 170.0, -135.0),     # Left eye left corner
                            (225.0, 170.0, -135.0),      # Right eye right corne
                            (-150.0, -150.0, -125.0),    # Left Mouth corner
                            (150.0, -150.0, -125.0)      # Right mouth corner
                        ], dtype = numpy.float32)
# Camera internals
camera_matrix = numpy.array(
                        [[height, 0.0, width/2],
                        [0.0, height, height/2],
                        [0.0, 0.0, 1.0]], dtype = numpy.float32
                        )

# Keeps a moving average of given length
def smooth_value(self, name, length, value):
    if not hasattr(self, 'smooth'):
        self.smooth = {}
    if not name in self.smooth:
        self.smooth[name] = numpy.array([value])
    else:
        self.smooth[name] = numpy.insert(arr=self.smooth[name], obj=0, values=value)
        if self.smooth[name].size > length:
            self.smooth[name] = numpy.delete(self.smooth[name], self.smooth[name].size-1, 0)
    sum = 0
    for val in self.smooth[name]:
        sum += val
    return sum / self.smooth[name].size


# Keeps min and max values, then returns the value in a ranve 0 - 1
def get_range(self, name, value):
    if not hasattr(self, 'range'):
        self.range = {}
    if not name in self.range:
        self.range[name] = numpy.array([value, value])
    else:
        self.range[name] = numpy.array([min(value, self.range[name][0]), max(value, self.range[name][1])] )
    val_range = self.range[name][1] - self.range[name][0]
    if val_range != 0:
        return (value - self.range[name][0]) / val_range
    else:
        return 0

def modal(self, context, event):

    if (event.type in {'RIGHTMOUSE', 'ESC'}) or self.stop == True:
        self.cancel(context)
        return {'CANCELLED'}

    if event.type == 'TIMER':
        self.init_camera()
        _, image = self._cap.read()
        if image is None:
            #cv2.imwrite("/Users/okadanaoki/Desktop/test.png", image)
            print("None")
            image = cv2.imread("/Users/okadanaoki/Desktop/800x600.jpg")

        print("image4");
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        rects = self.detector(gray, 0)
        # bpy.context.scene.frame_set(frame_num)

        # For each detected face, find the landmark.
        for (i, rect) in enumerate(rects):
            shape = self.predictor(gray, rect)
            shape = face_utils.shape_to_np(shape)

            #2D image points. If you change the image, you need to change vector
            image_points = numpy.array([shape[30],     # Nose tip - 31
                                        shape[8],      # Chin - 9
                                        shape[36],     # Left eye left corner - 37
                                        shape[45],     # Right eye right corne - 46
                                        shape[48],     # Left Mouth corner - 49
                                        shape[54]      # Right mouth corner - 55
                                    ], dtype = numpy.float32)

            dist_coeffs = numpy.zeros((4,1)) # Assuming no lens distortion

            if hasattr(self, 'rotation_vector'):
                (success, self.rotation_vector, self.translation_vector) = cv2.solvePnP(self.model_points, 
                    image_points, self.camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE, 
                    rvec=self.rotation_vector, tvec=self.translation_vector, 
                    useExtrinsicGuess=True)
            else:
                (success, self.rotation_vector, self.translation_vector) = cv2.solvePnP(self.model_points, 
                    image_points, self.camera_matrix, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE, 
                    useExtrinsicGuess=False)

            if not hasattr(self, 'first_angle'):
                self.first_angle = numpy.copy(self.rotation_vector)

            bones = bpy.data.objects["vincent_blenrig"].pose.bones

            bones["head_fk"].rotation_euler[0] = self.smooth_value("h_x", 3, (self.rotation_vector[0] - self.first_angle[0])) / 1   # Up/Down
            bones["head_fk"].rotation_euler[2] = self.smooth_value("h_y", 3, -(self.rotation_vector[1] - self.first_angle[1])) / 1.5  # Rotate
            bones["head_fk"].rotation_euler[1] = self.smooth_value("h_z", 3, (self.rotation_vector[2] - self.first_angle[2])) / 1.3   # Left/Right

            bones["mouth_ctrl"].location[2] = self.smooth_value("m_h", 2, -self.get_range("mouth_height", numpy.linalg.norm(shape[62] - shape[66])) * 0.06 )
            bones["mouth_ctrl"].location[0] = self.smooth_value("m_w", 2, (self.get_range("mouth_width", numpy.linalg.norm(shape[54] - shape[48])) - 0.5) * -0.04)
            bones["brow_ctrl_L"].location[2] = self.smooth_value("b_l", 3, (self.get_range("brow_left", numpy.linalg.norm(shape[19] - shape[27])) -0.5) * 0.04)
            bones["brow_ctrl_R"].location[2] = self.smooth_value("b_r", 3, (self.get_range("brow_right", numpy.linalg.norm(shape[24] - shape[27])) -0.5) * 0.04)

            bones["head_fk"].keyframe_insert(data_path="rotation_euler", index=-1)
            bones["mouth_ctrl"].keyframe_insert(data_path="location", index=-1)
            bones["brow_ctrl_L"].keyframe_insert(data_path="location", index=2)
            bones["brow_ctrl_R"].keyframe_insert(data_path="location", index=2)

            for (x, y) in shape:
                cv2.circle(image, (x, y), 2, (0, 255, 255), -1)

        cv2.imshow("Output", image)
        cv2.waitKey(1)

    return {'PASS_THROUGH'}

def init_camera(self):
    if self._cap == None:
        self._cap = cv2.VideoCapture(0)
        self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
        self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
        self._cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
        time.sleep(0.5)

def stop_playback(self, scene):
    print(format(scene.frame_current) + " / " + format(scene.frame_end))
    if scene.frame_current == scene.frame_end:
        bpy.ops.screen.animation_cancel(restore_frame=False)

def execute(self, context):
    bpy.app.handlers.frame_change_pre.append(self.stop_playback)

    wm = context.window_manager
    self._timer = wm.event_timer_add(0.02, window=context.window)
    wm.modal_handler_add(self)
    return {'RUNNING_MODAL'}

def cancel(self, context):
    wm = context.window_manager
    wm.event_timer_remove(self._timer)
    cv2.destroyAllWindows()
    self._cap.release()
    self._cap = None
def register():
bpy.utils.register_class(OpenCVAnimOperator)

def unregister():
bpy.utils.unregister_class(OpenCVAnimOperator)

if name == "main":
register()

# test call
#bpy.ops.wm.opencv_operator()

よろしくおねがします

※追記10/31
元のコードの80〜85行目を記載しておきます。

    if event.type == 'TIMER':
            self.init_camera()
            _, image = self._cap.read()
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            rects = self.detector(gray, 0)
            # bpy.context.scene.frame_set(frame_num)
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Kapustin

    2019/10/31 09:17

    ソースコードはマークダウン形式で貼り付けると見やすくなります。
    質問文は編集できますので、下記の「コードの挿入」を参考に質問文を修正してください。
    https://qiita.com/shizuma/items/8616bbe3ebe8ab0b6ca1

    キャンセル

  • キャンセル

回答 2

+1

最初とエラー内容が変わっていますか??
上記のコード83行目は if image is None: だと思うのですが、ここでエラーにはなりそうにありません。
追記のコードの83行目は gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  だと思うのですが、こちらでエラーになっている場合はカメラ画像が取得できていない(imageがNone)ために、エラーが発生しているように思います。

一度カメラ機能が正常に働いているかどうかのみをチェックしてみてください。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(True):
    ret, image = cap.read()
    cv2.imshow('frame',image)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

もう一点、怪しいと思われるのは init_camera 内で取得するカメラ画像の解像度を800x600に指定していますが、使用しているカメラはこの解像度の指定に対応していますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/01 18:48

    ```python
    def init_camera(self):
    if self._cap == None:
    self._cap = cv2.VideoCapture(1)
    self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
    self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
    self._cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
    time.sleep(0.5)
    ```
    こちらのコードのself._cap = cv2.VideoCapture(1)の部分の()の数字を-1から順に一ずつ足して行ったところ読み込みができました。パソコンによりますが、どうやら基本は0が内臓カメラで、そこから1ずれた値で外部カメラに切り替えることができるようです。

    キャンセル

check解決した方法

0

def init_camera(self):
if self._cap == None:
self._cap = cv2.VideoCapture(1)
self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
self._cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
time.sleep(0.5)


こちらのコードのself._cap = cv2.VideoCapture(1)の部分の()の数字を-1から順に一ずつ足して行ったところ読み込みができました。パソコンによりますが、どうやら基本は0が内臓カメラで、そこから1ずれた値で外部カメラに切り替えることができるようです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる