画像処理用のモジュールで、pip install opencv-python でモジュール追加が必要です。
import cv2 # OpenVC のカメラ利用
camera = cv2.VideoCapture(0) # カメラをオープンする(カメラ準備処理)
# カメラデバイスの IDを引数で指定するが、カメラが1台しか接続されていない場合は、0を指定
success, frame_img = camera.read() # カメラ画像取得する。
print(success, frame_img, frame_img.shape);
cv2.imshow("title", frame_img) # ウインドウ表示
code = cv2.waitKey(0) # 任意のキー入力を待つ
print( "%d '%c'"% (code , chr(code )) )
cv2.destroyAllWindows() # ウインドウ表示を終了
# ファイルの保存
cv2.imwrite("opencv1.png",frame_img)
cv2.imwrite("opencv1.jpg",frame_img)
上記は、カメラデバイスを開いて、撮影して、表示して、保存するコード
以下では、カメラ解像度を、小さい適当サイズに設定し、 その後でカメラ撮影と表示をループさせ、 希望の撮影画面で終了して、最後の撮影画面を .pngで保存させています。
import cv2 # OpenVC のカメラ利用
camera = cv2.VideoCapture(0) # カメラをオープンする(カメラ準備処理)
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 100) # 横幅設定で縦も自動設定される
print(f"FRAME_WIDTH :{ camera.get(cv2.CAP_PROP_FRAME_WIDTH) }")
print(f"FRAME_HEIGHT:{ camera.get(cv2.CAP_PROP_FRAME_HEIGHT) }")
while True:
success, frame_img = camera.read() # カメラ画像取得する。
if not success : break
cv2.imshow("title", frame_img) # ウインドウ表示
print(frame_img, frame_img.shape )
code = cv2.waitKey(500) # 0.5秒待つ
if code == ord('q'): break
cv2.destroyAllWindows() # ウインドウを閉じる
cv2.imwrite("opencv1.png",frame_img) # ファイルの保存
print(cv2.CAP_PROP_FRAME_WIDTH)で表示される番号が
CAP_PROP_FRAME_WIDTHプロパティに記憶される値です。
CAP_PROP_FRAME_WIDTH以外にどんなプロパティがあるかは、
次の表現で、リストに文字列で取得できます。
p_list=[x for x in dir(cv2) if x.startswith('CAP_PROP')]
# 上記で得られたのプロパティ文字列から動的にその値を取得するコードを以下に示す。
for i in range(len(p_list)):
p_name=p_list[i] # プロパティ名の文字列取得
p_idx=getattr(cv2, p_name) # # プロパティ名文字列から値を取得
print(p_name, camera.get(p_idx))
左が前述のコードで OpenCVを使って撮影で得られた画像で、中央が自作の左右反転関数で得た画像、右が左右反転プログラム失敗の画像です。

撮影画像、 左右反転プログラムで加工画像、 プログラムミスでできた画像
左の「opencv1.png」を、自作のmirror関数で左右反転させている。上記右画像は、失敗作のmirror_F関数を使った画像です。
import cv2 # OpenVC 利用
def mirror(frame_img): # ミラー反転の正しい関数定義例
for line in frame_img:
n = len(line) # 水平の画素数
print(n, frame_img.shape)
line2 = line.copy()
for i in range(n):
line[i] = line2[-i]
def mirror_F(frame_img): # ミラー反転しようとして失敗した定義例
for line in frame_img:
n = len(line) # 水平の画素数
for i in range(n//2):
tmp= line[i] # 交換のつもりで書いたが参照変数なので実態の交換できないという失敗
line[i] = line[-i]
line[-i] = tmp
frame_img = cv2.imread("opencv1.png") # 以前に撮影した画像の読み込み
print( frame_img.shape )
# frame_img = cv2.flip(frame_img, 1) # cv2の反転メソッドを使う場合
# mirror_F(frame_img) # 反転失敗の関数呼び出し
mirror(frame_img) # 正しく反転した関数呼び出し
cv2.imshow("processed image", frame_img) # ウインドウに画面表示
code = cv2.waitKey(0) # 押されるの待つ
cv2.destroyAllWindows() # ウインドウを閉じる
cv2.imwrite("opencv2.png",frame_img) # ファイルの保存
OpenCVには線分や四角(長方形、正方形)、円、楕円、円弧、矢印、マーカー、文字など、
様々な図形を描画する関数が用意されている。物体を検出した位置を図示したりする際などに便利。
右のような描画例のコード例を示します。
import cv2 # (opencvdraw.py)
import numpy as np
camera = cv2.VideoCapture(0) # カメラをオープンする(カメラ準備処理)
success, frame_img = camera.read() # カメラ画像取得する。
frame_img = np.zeros_like(frame_img)
if not success:frame_img = np.zeros( (300,400,3) , dtype=np.uint8) # カメラが無い場合は、この黒画像を使う
print(frame_img.shape)
h, w , bgr = frame_img.shape
# 各種描画テスト
cv2.rectangle(frame_img, (5, 5), (w-10, h-10), (0, 255, 255), thickness=-1)#矩形塗り
cv2.rectangle(frame_img, (5, 5), (w-10, h-10), (255, 0, 0), thickness=10)#枠
cv2.arrowedLine(frame_img, (0, 0), (200, 300), (0, 0, 255), thickness=4)#矢印
cv2.circle(frame_img, (100, 100), 50, (0, 255, 0), thickness=-1)#円塗り
cv2.ellipse(frame_img, ((300, 150), (50, 200), 45), (255, 255, 255), thickness=-1)# 楕円
cv2.ellipse(frame_img, (300, 150), (25, 100), 45,0,180, (0, 0, 0), thickness=5)# 楕円弧
cv2.drawMarker(frame_img, (30, 250), (0,0,0), markerSize=20) # +マークの描画
cv2.drawMarker(frame_img, (60, 250), (0,0,0), markerType=cv2.MARKER_TILTED_CROSS)
cv2.drawMarker(frame_img, (90, 250), (0,0,0), markerType=cv2.MARKER_STAR)
cv2.drawMarker(frame_img, (120, 250),(0,0,0), markerType=cv2.MARKER_SQUARE)
cv2.drawMarker(frame_img, (150, 250),(0,0,0), markerType=cv2.MARKER_TRIANGLE_UP)
cv2.drawMarker(frame_img, (180, 250),(0,0,0), markerType=cv2.MARKER_DIAMOND)
pts=np.array( [[200,100],[210,50],[220,150],[230,50],[240,150]])
cv2.polylines(frame_img,[pts], False, (255, 255, 0), thickness=2)
cv2.putText(frame_img,'Draw Text',(240,100),cv2.FONT_HERSHEY_SIMPLEX,
fontScale=1.0,color=(0,0,255),thickness=2)
cv2.line(frame_img, (200, 30), (400, 30), (0,0,0),thickness=2)
cv2.imshow("draw test", frame_img) # ウインドウ表示
print(frame_img, frame_img.shape )
code = cv2.waitKey(0)
cv2.destroyAllWindows() # ウインドウを閉じる
cv2.imwrite("opencvdraw.jpg",frame_img) # ファイルの保存
イベント登録関数function_nameは、次のように登録します。
cv2.setMouseCallback("name", function_name [, param ])
nameのウインドウ名は、cv2.imshowの第一引数のタイトル名と一致させなければならない。
paramは、必要に応じて指定し、function_nameの第4引数に使われます。
function_nameの仕様は、次の通りです。
def function_name (event, x, y, flags, param):
x,yはイベント発生座標、paramは引数を参照渡しすることで、内部を自由に変更できる。
eventの記憶内容を、以下定義と比較してイベントの種類が分かる。以下にその主要な定義を示す
| event | 意味 |
|---|---|
| cv2.EVENT_MOUSEMOVE | マウスを移動したとき,またはボタンを離した直後 |
| cv2.EVENT_LBUTTONDOWN | 左ボタンを押下したとき |
| cv2.EVENT_RBUTTONDOWN | 右ボタンを押下したとき |
| cv2.EVENT_MBUTTONDOWN | 中ボタンを押下したとき |
| cv2.EVENT_LBUTTONUP | 左ボタンを離したとき |
| cv2.EVENT_RBUTTONUP | 右ボタンを離したとき |
| cv2.EVENT_MBUTTONUP | 中ボタンを離したとき |
| cv2.EVENT_LBUTTONDBLCLK | 左ボタンがダブルクリックされたとき |
| cv2.EVENT_RBUTTONDBLCLK | 右ボタンがダブルクリックされたとき |
| cv2.EVENT_MBUTTONDBLCLK | 中ボタンがダブルクリックされたとき |
| flags | 値(バージョンで変わるかも)と意味 |
|---|---|
| cv2.EVENT_FLAG_LBUTTON | 1:左ボタンを押下したとき |
| cv2.EVENT_FLAG_RBUTTON | 2:右ボタンを押下したとき |
| cv2.EVENT_FLAG_MBUTTON | 3:中ボタンを押下したとき |
| cv2.EVENT_FLAG_CTRLKEY | 4:CTRLボタンを押した |
| cv2.EVENT_FLAG_SHIFTKEY | 8:SHIFTボタンを押した |
| cv2.EVENT_FLAG_ALTKEY | 32:ALTボタンを押した |
import cv2 # クリック操作でトリミングして保存(opencvevent.py)
import numpy as np
camera = cv2.VideoCapture(0) # カメラをオープンする(カメラ準備処理)
success, frame_img = camera.read() # カメラ画像取得する。
#frame_img = np.zeros_like(frame_img)
if not success:frame_img = frame_img = cv2.imread("opencv1.png") # カメラがない時に使う画像
print(frame_img.shape)
h, w , d = frame_img.shape
print([s for s in dir(cv2) if s.startswith('EVENT_')]) # イベント識別子を列挙
rect=[(0,0),(0,0)] # 矩形描画用
def cv2_win_event(event,x,y,flags,param): # mouse callback function
print(x,y,flags)
if event == cv2.EVENT_LBUTTONDOWN:
param[0]=(x,y)
elif event == cv2.EVENT_RBUTTONDOWN:
param[1]=(x,y)
cv2.namedWindow('eventtest', cv2.WINDOW_NORMAL)#第2引数で、ウインドウサイズ変更可能
cv2.setMouseCallback('eventtest',cv2_win_event,rect)
while True:
frame_img2 = frame_img.flatten().reshape( frame_img.shape )
cv2.rectangle(frame_img2,rect[0],rect[1],(0,0,255),thickness=5)#矩形描画
cv2.imshow("eventtest", frame_img2) # ウインドウ表示
#print(frame_img, frame_img.shape )
code = cv2.waitKey(1)
if code == 27: break
cv2.destroyAllWindows() # ウインドウを閉じる
cut_img = frame_img[rect[0][1]:rect[1][1], rect[0][0]:rect[1][0] ] # トリミング
cv2.imwrite("opencvevent.jpg",cut_img) # 切り取ったイメージのファイルの保存
物体検出に使われる識別器の1つで、「Haar Cascade」と呼ばれるアルゴリズムのクラスがcv2に組み込まれています。
これは、複数のカスケード分類器をカスケード接続した構成で、各分類器で positive または negative のどちらであるか判定が行われます。
そして、ある分類器で negative と判定された場合はその時点で棄却され、positive と判定された場合は、分類器の出力結果を次の分類器に渡します。
OpenCV では CascadeClassifier クラスでカスケード分類器が提供されています。
この引数にカスケードファイルと呼ばれる.xmlファイルを指定して使います。
この.xmlファイル群は、特徴量がすでに学習済みデータとして提供されています。
以下では、顔 (正面)判定用の「haarcascade_frontalface_default.xml」と
目判定用の「haarcascade_eye.xml」の利用例を示します。
なお、このファイルはpip install opencv-pythonのモジュールインストールで存在するはずです。
ですから、モジュールがあるディレクトリから探すとよいでしょう。
さて、『import モジュール名』 のコードでモジュール名を探して読み込まれますが、どののように探すのでしょうか?
実は、sys.path で得られるリスト内のディレクトリ内を探すようになっているのです。
例を以下に示します。
>>> import sys
>>> sys.path ['', 'R:\\Python36', R:\\Python36\\python36.zip', 'R:\\Python36\\DLLs', 'R:\\Python36\\lib', 'R:\\Python36\\lib\\site-packages', 'R:\\Python36\\lib\\site-packages\\win32', 'R:\\Python36\\lib\\site-packages\\win32\\lib', 'R:\\Python36\\lib\\site-packages\\Pythonwin']
>>>
この各ディレクトリ内を、プログラムで探して使ってみます。
所定のディレクトリ内からあるファイルを探す場合の例を示します
import glob
a=glob.glob("R:\\**\\*.xml", recursive=True)
# aに記憶されるパスは見つかったパスのリストになる。無い場合は[]
これを利用して、顔 (正面)判定用の「haarcascade_frontalface_default.xml」と 目判定用の「haarcascade_eye.xml」を探して、それぞれの判定機のCascadeClassifier クラスオブジェを生成して、 カメラの画面から見つかった顔と目を矩形で描画するプログラム例を示します。
import cv2 # opencvface.py
import sys
import os
import glob # ファイル探索用
face_cascade='haarcascade_frontalface_default.xml'# 顔のHaar Cascade識別器(分類器)
eye_cascade='haarcascade_eye.xml'
# 第一引数のディレクトリパス文字列のリスト内で、第二引数のファイル名を探して、
# 見つかったファイルの絶対パスを返す。(探索は、サブディレクトリ内も行う)
def searchFile( listDir, filename):
for dirPath in listDir:
if not os.path.isdir(dirPath): continue
searhpath=dirPath + os.sep + "**" + os.sep + filename
#print(searhpath)
listpath=glob.glob(searhpath, recursive=True)
if len(listpath)!=0: return listpath[0]
raise Exception(filename+"が見つからない")
face_cascade_path = searchFile(sys.path, face_cascade)
eye_cascade_path = searchFile(sys.path, eye_cascade)
print(face_cascade_path,"\n",eye_cascade_path)
# 分類器に、学習済のHaar-like特徴を用いたデータ(xmlファイル)をセット
face_cascade = cv2.CascadeClassifier(face_cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)
camera = cv2.VideoCapture(0)
while True:
success, frame_img = camera.read() # カメラ画像取得する。
gray = cv2.cvtColor(frame_img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
# 上記で顔と判断した矩形[x, y, w, h]の配列を得る。
# scaleFactorは1.0を超えるで大きいと縮小量で計算が速くなるが見逃しも増える(初期値1.1)
# minNeighborsが大きいと、見逃しは多いが誤検出は少ない(初期値3)
for x, y, w, h in faces:
cv2.rectangle(frame_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
face = frame_img[y: y + h, x: x + w]
face_gray = gray[y: y + h, x: x + w]
eyes = eye_cascade.detectMultiScale(face_gray)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(face, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
cv2.imshow('video image', frame_img)
key = cv2.waitKey(10)
if key == 27: # ESCキーで終了
break
camera.release()
cv2.destroyAllWindows()
def get_rect_image(img, x1,y1,x2,y2):
''' (x1,y1)は矩形の左上端、(x2,y2)右下端の座標 '''
return img[y1:y2,x1:x2]
def set_rect_image(img, px1, py1, rect_image):
# imgの px1, py1 の位置に rect_image を貼り付ける
sh,sw=img.shape[0],img.shape[1]
rh,rw=rect_image.shape[0],rect_image.shape[1]
img[py1:py1+rh, px1:px1+rw]=rect_image
img = cv2.imread( "ume5_426_240.png" ) # イメージファイルの読み取り
cv2.imshow("Capture", img) # 表示
key = cv2.waitKey(3000) # 3 秒待つ
rect_img=get_rect_image(img, x1=168,y1=48,x2=386,y2=310)
set_rect_image(img, px1=0, py1=0, rect_image=rect_img)
cv2.imshow("Capture", img) # 表示
key = cv2.waitKey(3000) # 3 秒待つ
cv2.imwrite( "ume5_426_240_2.png" , img) # イメージファイルの書き込み
異物検出の検討用に作ったものです。
左下の元画像ファイル(myroom.png)がある時、これを表示してクリック操作で、オーブを描き、右下のような画像を生成するプログラムです。
![]() |
![]() |
import cv2 # opencvevent2.py
import numpy as np
frame_img = cv2.imread("myroom.png") # 加工対象の画像
print(frame_img.shape)
h, w , d = frame_img.shape
param={"img":None, "x":0,"y":0} # 描画情報
def addPixel(BGR,val):# 各色の値にvalを加算(BGRの画素に明暗を施す)
if val >= 0:
m = map( lambda x : 255 if x+val > 255 else x+val, BGR )
else:
m = map( lambda x : 0 if x+val < 0 else x+val, BGR )
return np.array(list(m), np.uint8)
def putOrb(img, x,y,numb, radius, val=10): # オーブを生成
''' numb個の点にvalの明暗を設定 '''
xs = np.random.normal(x,radius*2.5,numb).astype(np.int)
ys = np.random.normal(y,radius*2.5,numb).astype(np.int)
pts = list(zip(xs,ys))
for px,py in pts:
if px >= 0 and px < img.shape[1] and py >= 0 and py < img.shape[0]:
distance = np.sqrt((px-x)**2 + (py-y)**2)
if distance > radius: continue
bgr=addPixel(img[py][px], val)
img[py][px]=bgr
print(f"px={px:3},py={py:3},{bgr}")
# mouse callback function
def cv2_win_event(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:# オーブ中心座標指定
print(x,y,flags)
param["x"], param["y"] = x,y
if event == cv2.EVENT_RBUTTONDOWN:# オーブ半径指定で、描画処理
px, py = param["x"],param["y"]
radius=np.sqrt((px-x)**2 + (py-y)**2)
numb = int(5000*radius)
putOrb(param["img"],px,py,numb=numb,radius=radius,val=2)#オーブ生成
cv2.namedWindow('eventtest', cv2.WINDOW_NORMAL)#第2引数で、サイズ変更可能
cv2.setMouseCallback('eventtest',cv2_win_event,param)
param["img"]=frame_img
while True:
cv2.imshow("eventtest", frame_img) # ウインドウ表示
#print(frame_img, frame_img.shape )
code = cv2.waitKey(1)
if code == 27: break # ESC で終了
cv2.destroyAllWindows() # ウインドウを閉じる
cv2.imwrite("myroom2.png",frame_img) # 加工したイメージのファイルの保存