# pyplotグラフをinline表示するためのための Magic Command
%matplotlib inline
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import cv2
# 画像ファイル名の指定
src = 'colors.bmp'
確認に使用するs'colors.bmp'は width(8)*height(4)画素の24bitBitMap(左から BGRCMYGWの並び)
![]()
# PILに読込んでpyplotで表示する例(正しく表示できる例)
# 画像の読み込み
im_pil = Image.open(src)
# 画像をarrayに変換
im_list = np.asarray(im_pil)
# 貼り付け
plt.imshow(im_list)
# 表示
plt.show()
# OpenCVで読込んでpyplotで表示する例1(チャネル順の不整合)
# 画像の読み込み
im_ocv = cv2.imread(src)
# 貼り付け
plt.imshow(im_ocv)
# 表示
plt.show()
OpenCVでは1画素内を(B,G,R)の順で格納しているが、pyplotは(R,G,B)の順が前提なので
pyplotで表示すると、RチャネルとBチャネルが入れ替わってることに注意!
# OpenCVで読込んでpyplotで表示する例2(正しいチャネル順)
# 画像の読み込み
im_ocv = cv2.imread(src)
# OpenCVでは (B,G,R) で格納しているが、matplotlibでは (R,G,B) で画像を認識するので表示用にチャネル変換
img = cv2.cvtColor(im_ocv, cv2.COLOR_BGR2RGB)
# 貼り付け
plt.imshow(img)
# 表示
plt.show()
OpenCVでのカラー画像格納形式は
[0行目, 1行目, 2行目, ... , (Y-1)行目] → よって、len( im_ocv )は画像の height(Y) となる。
各行は
[0列目, 1列目, 2列目, ... , (X-1)列目] → よって、len( im_ocv[0] )は画像の width(X) となる。
各画素には各チャネルの値が格納されており、カラー画像の場合は、
len( im_ocv[0][0] ) → 3 ※(0,0)の画素にはB,G,Rの3つの値が格納されている意
print( im_ocv[2][1][0] ) は Y=2,X=1のBチャネル値を示す。
配列の格納イメージは下図参照。

# Y=2ラインのBチャネルの値を変更して表示確認する
print(im_ocv[2])
# Loopによる1画素ずつの変更
for x in range(8):
im_ocv[2][x][0] = 32
# チャネル交換して貼り付け
img = cv2.cvtColor(im_ocv, cv2.COLOR_BGR2RGB)
plt.imshow(img)
# 表示:Y=2のラインの色が変わっていることを確認
plt.show()
# 変更後の中身を確認
print(im_ocv[2])
# for文を使わずにY=2ラインのBチャネルを一括で書き換える
im_ocv[2,:,0] = 250
img = cv2.cvtColor(im_ocv, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.show()
# 同様にSliceで縦方向(Xライン)に一括で書き換える
# X=7ラインのGチャネルを一括書き換え
im_ocv[:,7,1] = 8
# pyplot用に変換して表示
img = cv2.cvtColor(im_ocv, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.show()
# 画像をHSVに変換しての操作例
im_hsv = cv2.cvtColor(im_ocv, cv2.COLOR_BGR2HSV)
# arrayの並びは[Y, X, [H,S,V]]
# OpenCVでは、Hueは0~180で1周なので注意
im_hsv[1, :, 0] = 20
# 表示用にRGBに変換
im_hsv = cv2.cvtColor(im_hsv, cv2.COLOR_HSV2RGB)
plt.imshow(im_hsv)
plt.show()
# 適当な画像を読込んで表示
img = cv2.imread('test2.bmp')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 画像表示
plt.show()
# Y座標を固定して横断面波形を描画
target_y = 400
line_blue = img[target_y, :, 0]
line_green = img[target_y, :, 1]
line_red = img[target_y, :, 2]
# 線色はrgbcmykwが指定可能
plt.plot(line_blue, 'b-', label="Blue", lw=1) # 実線
plt.plot(line_green, 'g--', label="Green", lw=1) # 破線
plt.plot(line_red, 'r.-', label="Red", lw=1) # dot + 実線
# grid表示
plt.grid()
# Label表示
plt.legend()
# グラフ表示
plt.show()
# x座標を固定して横断面波形を描画
target_x = 300
line_blue = img[:, target_x, 0]
line_green = img[:, target_x, 1]
line_red = img[:, target_x, 2]
# 画像Yサイズを.shapeで取得してグラフY軸に指定
height = img.shape[0]
plt.plot(line_blue, range(height), 'b+-', label="Blue", lw=1) # cross + 実線
plt.plot(line_green, range(height), 'g*:', label="Green", lw=1) # star + 点線
plt.plot(line_red, range(height), 'r:', label="Red", lw=1) # 点線
# Y軸反転
plt.gca().invert_yaxis()
# grid表示
plt.grid()
# Label表示
plt.legend()
# グラフ表示
plt.show()
# 画像上に任意の直線を描画(1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
x0, y0, x1, y1 = 10, 50, 600, 400
plt.plot([x0, x1], [y0, y1], 'y-', lw=2)
# 表示 -> 画像周囲に余白が出来てしまう
plt.show()
# 画像上に任意の直線を描画(2)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
x0, y0, x1, y1 = 10, 50, 600, 400
plt.plot([x0, x1], [y0, y1], 'y-', lw=2)
# 画像のサイズ取得して軸範囲を明示的に指定
height, width = img.shape[0], img.shape[1]
plt.ylim(0, height)
plt.xlim(0, width)
# Y軸反転:ylim()することで画像が天地逆になるため反転表示する
plt.gca().invert_yaxis()
# 表示 -> 正しく表示できた
plt.show()
# 元の画像と断面波形を並べて描画
# 波形を得るライン
target_x = 300
target_y = 75
# 画像のサイズ
height = img.shape[0]
width = img.shape[1]
# 背景に白を指定しておくと後でグラフ画像をCopyする時に透過しないので楽
# 並べて描画するので figsize を大きくしておく
fig = plt.figure(figsize=(10,8), facecolor='w')
# ---- 元画像 ----
ax1 = fig.add_subplot(2, 2, 1) # 2x2の第二象限
ax1.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 断面波形取得位置に線描画(この引き方だと余白は付かない)
ax1.axhline(y=target_y, color='cyan') # 水平線
ax1.axvline(x=target_x, color='yellow') # 垂直線
# ---- 断面波形(Y指定) ----
line_blue = img[target_y, :, 0]
line_green = img[target_y, :, 1]
line_red = img[target_y, :, 2]
# plotとgrid表示
ax3 = fig.add_subplot(2,2,3) # 2x2の第三象限
ax3.plot(line_blue, 'b-', label="Blue", lw=1)
ax3.plot(line_green, 'g-', label="Green", lw=1)
ax3.plot(line_red, 'r-', label="Red", lw=1)
ax3.grid()
# Label表示
ax3.legend()
# ---- 断面波形(X指定) ----
line_blue = img[:, target_x, 0]
line_green = img[:, target_x, 1]
line_red = img[:, target_x, 2]
# plotとgrid表示
ax2 = fig.add_subplot(2,2,2) # 2x2の第一象限
ax2.plot(line_blue, range(height), 'b-', label="Blue", lw=1)
ax2.plot(line_green, range(height), 'g-', label="Green", lw=1)
ax2.plot(line_red, range(height), 'r-', label="Red", lw=1)
ax2.grid()
# Y軸反転(0を上側に)
ax2.invert_yaxis()
# Label表示
ax2.legend()
# ---- 表示 ----
plt.show()
# 切出し矩形座標
x0, y0 = 320, 20 # 始点
ww, hh = 150, 100 # width, height
# 切出し
img_trim = img[y0:(y0+hh), x0:(x0+ww)]
# 並べて表示
fig = plt.figure(figsize=(10,8), facecolor='w')
ax1 = fig.add_subplot(1, 2, 1)
ax1.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax1.add_patch(plt.Rectangle(xy=(x0,y0), width=ww, height=hh, color='r', fill=False))
ax1.set_title('Original')
ax2 = fig.add_subplot(1, 2, 2)
ax2.imshow(cv2.cvtColor(img_trim, cv2.COLOR_BGR2RGB))
ax2.set_title('Trim')
plt.show()
# Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray)
plt.gray()
plt.show()
# 輝度ヒストグラム:flatten()で画像を1次元配列に変換。binsは棒(区間)の数。
plt.hist(gray.flatten(), bins=64)
plt.show()
# 画像の2値化
## 全域同閾値で2値化
th = 20 # th以上を「白」閾値
ret, binary1 = cv2.threshold(gray, th, 255, cv2.THRESH_BINARY)
th = 90 # 閾値
ret, binary2 = cv2.threshold(gray, th, 255, cv2.THRESH_BINARY)
## 大津の2値化
ret,th_otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
## 8近傍平均値で2値化
th_mean = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
## 8近傍重み付き(Gaussian)平均値で2値化
th_gaus = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
### 並べて表示
fig = plt.figure(figsize=(10,6), facecolor='w')
ax1 = fig.add_subplot(2, 3, 1) # 2行3列の1番目
ax1.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax1.set_title('Original')
ax2 = fig.add_subplot(2, 3, 2) # 2行3列の2番目
ax2.imshow(binary1)
ax2.set_title('Th:Low')
ax3 = fig.add_subplot(2, 3, 3) # 2行3列の3番目
ax3.imshow(binary2)
ax3.set_title('Th:High')
ax4 = fig.add_subplot(2, 3, 4) # 2行3列の3番目
ax4.imshow(th_otsu)
ax4.set_title('Otsu')
ax5 = fig.add_subplot(2, 3, 5) # 2行3列の5番目
ax5.imshow(th_mean)
ax5.set_title('neighbor8_Mean')
ax6 = fig.add_subplot(2, 3, 6) # 2行3列の6番目
ax6.imshow(th_gaus)
ax6.set_title('neighbor8_Gaussian')
plt.show()
# 膨張と収縮
## 8近傍の定義
neiborhood8 = np.array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]], np.uint8)
# 並べて表示
fig = plt.figure(figsize=(10,8), facecolor='w')
ax1 = fig.add_subplot(1, 3, 1)
ax1.imshow(binary2)
ax1.set_title('Th:High')
img_erosion = cv2.erode(binary2, neiborhood8, iterations=5) # 収縮5回
ax3 = fig.add_subplot(1, 3, 2)
ax3.imshow(img_erosion)
ax3.set_title('Bin->Erosion')
img_dilation = cv2.dilate(img_erosion, neiborhood8, iterations=5) # 膨張5回
ax2 = fig.add_subplot(1, 3, 3)
ax2.imshow(img_dilation)
ax2.set_title('Bin->Erosion->Dilation')
plt.show()
# メディアンフィルタ
# 処理前
plt.imshow(cv2.cvtColor(img_trim, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.show()
fsize = 3
median = cv2.medianBlur(img_trim, fsize)
# 処理後
plt.imshow(cv2.cvtColor(median, cv2.COLOR_BGR2RGB))
plt.title('Median')
plt.show()
# 2D Convolution
## 5x5平均
kernel = np.array([[ 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1],
[ 1, 1, 1, 1, 1]], np.float32) / 25.0
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor(results, cv2.COLOR_BGR2RGB))
plt.title('Average')
plt.show()
## 5x5ガウシアン
kernel = np.array([[ 1, 4, 6, 4, 1],
[ 4, 16, 24, 16, 4],
[ 6, 24, 36, 24, 6],
[ 4, 16, 24, 16, 4],
[ 1, 4, 6, 4, 1]], np.float32) / 256.0
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor(results, cv2.COLOR_BGR2RGB))
plt.title('Gaussian')
plt.show()
## ラプラシアン
kernel = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]], np.int16) # int8でもいいのかもしれないが余裕見て
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor( results, cv2.COLOR_BGR2RGB))
plt.title('Laplacian')
plt.show()
## ソーベル:左から右へ
kernel = np.array([[ 1, 0, -1],
[ 2, 0, -2],
[ 1, 0, -1]], np.int16)
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor(results, cv2.COLOR_BGR2RGB))
plt.title('Sobel Left to Right')
plt.show()
## ソーベル:上から下へ
kernel = np.array([[ 1, 2, 1],
[ 0, 0, 0],
[-1, -2, -1]], np.int16)
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor(results, cv2.COLOR_BGR2RGB))
plt.title('Sobel Upper to Bottom')
plt.show()
## ソーベル:右下から左上へ
kernel = np.array([[-2, -1, 0],
[-1, 0, 1],
[ 0, 1, 2]], np.int16)
results = cv2.filter2D(img_trim, -1, kernel)
plt.imshow(cv2.cvtColor(results, cv2.COLOR_BGR2RGB))
plt.title('Sobel RB to LU')
plt.show()
# Cannyによるエッヂ抽出
results = cv2.Canny(img_trim, 450, 50) # 2番目引数と3番目引数は入替えても結果同じ
plt.imshow(results)
plt.title('Canny edge')
plt.show()