Python 画像処理

OpenCVによる画像表示

インストール

pip install opencv-python

画像の表示

image1.py に記述

import cv2
import os

# 画像の読み込み
path = os.path.dirname(__file__) + '/sample.jpg'
img = cv2.imread(path)

# 画像の表示
cv2.imshow('img', img)
cv2.waitKey(0)

画像の保存

path = os.path.dirname(__file__) + '/sample2.png'
cv2.imwrite(path, img)

加工

image2.pyに以下を記述

import cv2
import os

# 画像の読み込み
path = os.path.dirname(__file__) + '/sample.jpg'
img = cv2.imread(path)

# 画像の加工

# 画像の表示
cv2.imshow('img', img)
cv2.waitKey(0)

画像の加工のところに随時貼り付けていく。試したコードはコメントにして次を試す(Ctrl+/ でコメント化可能)。

サイズ変更

print(img.shape) # 現在のサイズ

400×400に変更

img = cv2.resize(img,(400,400)) # サイズ変更

切り抜き

[y1:y2, x1:x2]で左上x1,y1から右下x2,y2を切り抜く。

img = img[0:500, 100:400]  # 切り抜き 100,0 ~ 400,500

方向反転

img = cv2.flip(img, 0) # 上下反転
img = cv2.flip(img, 1) # 左右反転

ネガポジ反転

img = 255 - img # ネガポジ反転

グレイスケール

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # グレイスケール

ぼかし

img = cv2.blur(img, (10,10)) # ぼかし

一部分をぼかす(切り出し->ぼかし->合成)

img2 = img[0:500, 100:400]  # 切り出し
img2 = cv2.blur(img2, (10,10)) # ぼかし
img[0:500, 100:400] = img2 # 合成

しきい値処理

ある数(しきい値)より大きいドットを全て同じ色にする。

cv2.threshold(画像, しきい値, 置換後の値, 処理方法)

※処理方法はcv2.THRESH_BINARYが一般的
※戻り値はタプル。2つ目に画像が入る。

_,img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

描画

image3.pyに以下を記述

import cv2
import os

# 画像の読み込み
path = os.path.dirname(__file__) + '/sample.jpg'
img = cv2.imread(path)

# 画像の描画

# 画像の表示
cv2.imshow('img', img)
cv2.waitKey(0)

画像の描画のところに随時貼り付けていく。試したコードはコメントにして次を試す(Ctrl+/ でコメント化可能)。

直線

# 直線 line(画像, 始点, 終点, 色, 線の太さ)
cv2.line(img, (100,100), (200,200), (0, 0, 255), 2)

※色はBGRの順に指定。上の例は赤

四角形

# 四角形 rectangle(画像, 左上, 右下, 色, 線の太さ)
cv2.rectangle(img, (100, 100), (200,200), (0, 0, 255), 2)

※線の太さはマイナスなら塗りつぶし

# 円 circle(画像, 中心, 半径, 色, 線の太さ)
cv2.circle(img, (200, 200), 100, (0, 0, 255), 2)

※線の太さはマイナスなら塗りつぶし

モザイク処理

関数を自作。一度小さくして大きくする。

def mosaic(img, rect, size):
    (x1, y1, x2, y2) = rect
    width = x2 - x1
    height = y2 - y1
	
    image_rect = img[y1:y2, x1:x2]
    image_small = cv2.resize(image_rect, (size, size))
    image_mos = cv2.resize(image_small, (width, height), interpolation=cv2.INTER_AREA)
	
    img2 = img.copy()
    img2[y1:y2, x1:x2] = image_mos
    
    return img2

img = mosaic(img, (100,100, 300,300), 10)

カメラからの入力

VideoCaptureオブジェクト

カメラからVideoCaptureオブジェクトの取得

cap = cv2.VideoCapture(カメラ番号, cv2.CAP_DSHOW)

最初の引数はカメラ番号。通常0から順に割り当てられる。

2番目の引数ではDirectSHOWオプションを指定する(Windows10以降では必須)。

メソッド

read()画像読み込み。タプルを返し、2番目が画像。
release()キャプチャ終了(カメラ解放)

カメラ入力を画像表示

camera.py に記述

import cv2

# カメラから入力開始
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

while True:
    # カメラの画像を読み込む
    result, frame = cap.read()
    
    # 画像を縮小
    frame = cv2.resize(frame, (500,300))
    
    # ウィンドウに画像を出力 
    cv2.imshow('Camera', frame)

    if cv2.waitKey(1) == 27:  # 1ミリ秒待ち。ESCキーで終了
        break

cap.release() # カメラを解放
cv2.destroyAllWindows() # ウィンドウを破棄

顔検出

face.py に記述

カスケードファイルを用意する。
人間の顔は haarcascade_frontalface_default.xml を入手(↓のボタンを押してダウンロードし、コピー)。

import cv2
import os

# 画像の読み込み
path = os.path.dirname(__file__)
img = cv2.imread(path + '/sample.jpg')

# グレイスケールに
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# カスケードファイルの読み込み
face_cascade = cv2.CascadeClassifier(path + '/haarcascade_frontalface_default.xml')

# 顔認証
faces = face_cascade.detectMultiScale(gray)

# 座標を取得
for (x, y, width, height) in faces:
    # 顔部分を四角で囲う
    cv2.rectangle(img, (x, y), (x+width, y+height), (255, 0, 0), 2)

cv2.imshow('img', img)
cv2.waitKey(0)

カメラによる認識

face_camera.py に記述

import cv2
import os

path = os.path.dirname(__file__)
face_cascade = cv2.CascadeClassifier(path + '/haarcascade_frontalface_default.xml')
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

while True:
   ret, img = cap.read()
   gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
   faces = face_cascade.detectMultiScale(gray)
   
   for (x, y, width, height) in faces:
      cv2.rectangle(img, (x, y), (x+width, y+height), (255, 0, 0), 2)
     	 
   cv2.imshow('camera', img)

   if cv2.waitKey(10) == 27:
      break

cap.release()
cv2.destroyAllWindows()

物体検出

YOLO v11を使用し画像内の物体を検出する。

準備

pip install ultralytics

物体検出

yolo1.py に記述

from ultralytics import YOLO
import cv2

# モデルの生成
model = YOLO('yolo11n.pt')

# 判定
results = model.predict('https://ultralytics.com/images/bus.jpg')
img = results[0].plot()

# 表示
cv2.imshow('img', img)
cv2.waitKey(0)

以下のように行うと /runs/detect/predice 配下に判定画像が格納される

yolo2.py に記述

from ultralytics import YOLO

# モデルの生成
model = YOLO('yolo11n.pt')

# 判定
model.predict('https://ultralytics.com/images/bus.jpg', save=True)

カメラによるリアルタイム判定

yolo_camera.py に記述

from ultralytics import YOLO
import cv2

cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

model = YOLO("yolo11n.pt")

while True:
    success, img = cap.read()

    results = model.predict(img)
    frame = results[0].plot()

    cv2.imshow("YOLO", frame)
    
    if cv2.waitKey(5) == 27:
        break

検出できる物体

yolo3.py に記述

from ultralytics import YOLO

# モデルの生成
model = YOLO('yolo11n.pt')

# 物体検出できるものを表示
print(model.names)

結果

{0: 'person', 1: 'bicycle', 2: 'car', 3: 'motorcycle', 4: 'airplane', 5: 'bus', 6: 'train', 7: 'truck', 8: 'boat', 9: 'traffic light', 10: 'fire hydrant', 11: 'stop sign', 12: 'parking meter', 13: 'bench', 14: 'bird', 15: 'cat', 16: 'dog', 17: 'horse', 18: 'sheep', 19: 'cow', 20: 'elephant', 21: 'bear', 22: 'zebra', 23: 'giraffe', 24: 'backpack', 25: 'umbrella', 26: 'handbag', 27: 'tie', 28: 'suitcase', 29: 'frisbee', 30: 'skis', 31: 'snowboard', 32: 'sports ball', 33: 'kite', 34: 'baseball bat', 35: 'baseball glove', 36: 'skateboard', 37: 'surfboard', 38: 'tennis racket', 39: 'bottle', 40: 'wine glass', 41: 'cup', 42: 'fork', 43: 'knife', 44: 'spoon', 45: 'bowl', 46: 'banana', 47: 'apple', 48: 'sandwich', 49: 'orange', 50: 'broccoli', 51: 
'carrot', 52: 'hot dog', 53: 'pizza', 54: 'donut', 55: 'cake', 56: 'chair', 57: 'couch', 58: 'potted plant', 59: 'bed', 60: 'dining table', 61: 'toilet', 62: 'tv', 63: 'laptop', 64: 'mouse', 65: 'remote', 66: 'keyboard', 67: 'cell phone', 68: 'microwave', 69: 'oven', 70: 'toaster', 71: 'sink', 72: 'refrigerator', 73: 'book', 74: 'clock', 75: 'vase', 76: 'scissors', 77: 'teddy bear', 78: 'hair drier', 79: 'toothbrush'}

predictを行う際にclassesを指定して物体検出できるものを絞り込むことができる

yolo3.py の下に記述

# personとcarのみ
results = model.predict('https://ultralytics.com/images/bus.jpg', classes=[0, 2])

# 表示
img = results[0].plot()

import cv2
cv2.imshow('img', img)
cv2.waitKey(0)

検出結果の取得

yolo4.py に記述

from ultralytics import YOLO

# モデルの生成
model = YOLO('yolo11n.pt')

# 判定
results = model.predict('https://ultralytics.com/images/bus.jpg')

# 検出したオブジェクトの座標とクラス取得
for box in results[0].boxes:
    x1, y1, x2, y2 = [int(i) for i in box.xyxy[0]]
    
    # クラスIDを取得
    class_id = int(box.cls[0])
    
    # クラス名を取得
    class_name = model.names[class_id]
    
    # 信頼度を取得
    confidence = float(box.conf[0])
    
    print(f"{class_name}, StartX={x1}, StartY={y1}, EndX={x2}, EndY={y2}, 信頼度: {confidence:.2f}")

YOLOでの学習

YOLOで独自の物体を検出できるようにするにはモデルのトレーニングを行います。

フォルダと画像の準備

作業用フォルダに以下を作成します。

imagesとlabelsフォルダ内にはさらにtrainフォルダとvalフォルダを作成します。

images内のtrainに訓練用の画像ファイル、images内のvalに検証用の画像ファイルを置きます。ファイル数は20%程度を検証用に置くと良いでしょう(80枚と20枚など)。

なお、画像ファイルは幅640ピクセルなどの一定サイズでそろえます。

data.yaml には以下を記述します。

# 作業用フォルダ(imagesとlabelsフォルダがある場所)のパス
path: c:/yolo/

# 訓練用画像のフォルダ
train: images/train  

# 検証用画像のフォルダ
val: images/val 

# 種類数
nc: 2  

# 種類名のリスト
names: ['dog', 'cat']  

アノテーション設定

画像のどこに認識したい物体があるかを指定します。これをアノテーション設定と呼びます。
アノテーションファイルを作成するためのツールを使用します。
WebベースのMAKE SENSEやローカルで使用するlabelImgがあります。

MAKE SENSE https://www.makesense.ai/

Get Started をクリックし、画像ファイルをドラッグ&ドロップするか、クリックして画像ファイルを指定します。

Object Detectionをクリックし、Create Labelの画面で+を推してラベル(物体名)を種類数分設定します(dog,catなど)。追加し終わったら Start Project を押します。

画像を選択し、画像中の物体を枠で囲み、右側からラベルを選びます。これを全ファイルを行います。

全ファイル終わったら、ActionsからExport Annotationsを選び、「A.zip package files in YOLO format」を選択し、Exportボタンでダウンロードします。

ダウンロードされたzipファイルの中身を対応するlabelsフォルダにコピーします。

labelImg

labelImgをインストールします。

pip install labelImg

ただ、現状のlabelImg1.8.6にはバグが有り、動作しません。そのため、修正を行います。

pip show labelImg

これで表示されたフォルダ内のlabelImg内のlabelImg.pyで以下を修正します。

labelImg.pyの965行目
bar.setValue(int(bar.value() + bar.singleStep() * units))

また、libsフォルダ内のcanvas.pyで以下を修正します。

canvas.pyの526行目
p.drawRect(int(left_top.x()), int(left_top.y()), int(rect_width), int(rect_height))
canvas.pyの530行目
p.drawLine(int(self.prev_point.x()), 0, int(self.prev_point.x()), int(self.pixmap.height()))
canvas.pyの531行目
p.drawLine(0, int(self.prev_point.y()), int(self.pixmap.width()), int(self.prev_point.y()))

アノテーション設定

labelImgで画像を読み込み、物体を指定します。これをアノテーション設定と呼びます。

コマンドラインで labelImge と入力して起動します。

「Open Dir」ボタンで画像のあるフォルダ(images)を指定します。
「Chage Save Dir」ボタンでアノテーションを保存するフォルダ(labels)を指定します。
「PASCAL VOC」になっているボタンを押し「YOLO」に変更します。

画像が表示されますので、「Create ReactBox」ボタンを押し、マウスをドラッグしてどこにその物体が映っているかを指定します。すると、クラス名(物体名)を入力するウィンドウが表示されるので入力します(英数字推奨)。

物体の指定が終わったら「Save」ボタンで保存します。

これを全ファイル行います。なお、dキーで次の画像、wキーで「Create ReactBox」ボタン、Ctrl+Sで保存ができます。

学習

アノテーション設定が終わったらコマンドを入力し、学習を行います。
yolo detect train model=yolo11n.pt data=data.yaml epochs=50 imgsz=640
dataにはyamlファイル、epochsには学習エポック数、imgszには画像サイズを設定します。 学習が終わるとrunsフォルダのdetect/train/weightsにbest.ptとlast.ptが生成されます。last.ptは最後に保存されたモデル、best.ptは最も性能が良かったモデルです。 モデルを使用し、表示してみます。
from ultralytics import YOLO
import cv2

# モデルの生成
model = YOLO('runs/detect/train/weights/best.pt')

# 判定
results = model.predict('テスト用画像ファイル')
img = results[0].plot()

# 表示
cv2.imshow('img', img)
cv2.waitKey(0)