本記事では、画像ファイルの物体の輪郭を抽出する雛形コードを載せました。検出手法を関数として4つ載せました。いずれも処理の大まかな流れは、2値化してしきい値で判別します。その2値化が画像に依っては難しいのです。
そのため、毛色の異なる次の2つのリンク先の画像とコードを参考にさせて頂きました。更に、自前で準備した画像2つを合わせた計4つに対して、処理の過程と共に結果例を順番に載せます。
▼チューリップの花の輪郭を検出する
(参考リンク)オブジェクト輪郭検出 | OpenCV / findContours を使用して画像中のオブジェクトの輪郭を検出する方法
画像の特徴1:花が沢山あって、それぞれの形状が複雑
画像の特徴2:花とそれ以外の葉っぱなどの背景との色度が明確にわかれている
処理1. 色調RGBをHSVへ変更
→茎と土の輪郭をぼかす
処理2. ガウシアンによるスムージング処理
→更に、茎や土周りの輪郭をぼかす
処理3. HSV色空間から、H(色相)のデータを抽出する
→ほぼ、花とそれ以外で色が分かれた。
処理4. マスクの作成
→しきい値が明確になったため、明確にマスクを掛ける
処理5. オリジナル画像を元に、チューリップ花の輪郭を描画する
→ほぼ、間違いなく花を検出している。
検出した輪郭の座標をcsvファイルに保存する仕様にしてるため、下図のようにグラフ化できるし、必要箇所の形だけ抽出したりといったことも可能になる。
▼微生物(カイミジンコ?)の外形を検出する
(参考リンク)Python - opencv python 輪郭の特徴点の座標|teratail
画像の特徴1:色は黄土色の一色(顕微鏡の光によると思われる)
画像の特徴2:そのため、線だけ構成されている
処理1. 白黒化の処理
処理2. 大津処理
処理3. 膨張縮小によるぼかし
処理4. 微生物の輪郭を描写
座標もcsvファイルへ出力する仕様
▼以降は、自前で準備した画像
下図はパワポの図形で作成した画像です。
輪郭が明確なため、下図のようにほぼ忠実に検出しています。
下図は、ビールとおつまみの写真です。上記1~3とは異なり輪郭を識別しづらいため、物体の境界を明確化する処理に更なる工夫が必要になることがわかる。
■本プログラム
本コードの仕様を次に3つ示します。
・仕様1. 輪郭検出の設定(手法の選択と条件の組み合わせ)を4つの関数として、順番に全て実行する
・仕様2. 本コード実行前に、カレントディレクトリにある「.jpg」ファイルをリストで取得して、順場に実行する
・仕様3. 途中の処理で識別できない場合でもエラーで落ちずに、次のループへ回す
import glob
import cv2
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.patches import Polygon
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")
def draw_contours_A(file_name, img):
height, width = img.shape[:2]
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imwrite(now + '_1_' + file_name + '_hsv_A.jpg', hsv)
gauss = cv2.GaussianBlur(hsv,(9, 9),3)
cv2.imwrite(now + '_2_' + file_name + '_gauss_A.jpg', gauss)
img_H, img_S, img_V = cv2.split(gauss)
cv2.imwrite(now + '_3_' + file_name + '_H_of_HSV_A.jpg', img_H)
_thre, img_mask = cv2.threshold(img_H, 140, 255, cv2.THRESH_BINARY)
cv2.imwrite(now + '_4_' + file_name + '_mask_A.jpg', img_mask)
contours, hierarchy = cv2.findContours(img_mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
x_list = []
y_list = []
for i in range(0, len(contours)):
if len(contours[i]) > 0:
if cv2.contourArea(contours[i]) < 500:
continue
cv2.polylines(img, contours[i], True, (255, 255, 255), 5)
buf_np = contours[i].flatten()
for i, elem in enumerate(buf_np):
if i%2==0:
x_list.append(elem)
else:
y_list.append(elem*(-1))
cv2.imwrite(now + '_5_' + file_name + '_boundingbox_A.jpg', img)
x_df = pd.Series(x_list)
y_df = pd.Series(y_list)
DF = pd.concat((x_df.rename(r'#X'), y_df.rename('Y')), axis=1, sort=False)
DF.to_csv(now + '_' + file_name + "_target_contour_A.csv", encoding="utf-8", index=False)
def draw_contours_B(file_name, img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite(now + '_1_' + file_name + '_gray_B.jpg', gray)
ret,th1 = cv2.threshold(gray,200,255,cv2.THRESH_BINARY)
cv2.imwrite(now + '_2_' + file_name + '_th1_B.jpg', gray)
contours, hierarchy = cv2.findContours(th1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
areas = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 10000:
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
areas.append(approx)
cv2.imwrite(now + '_3_' + file_name + '_boundingbox_B.jpg', img)
def draw_contours_C(file_name, img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite(now + '_1_' + file_name + '_gray_C.jpg', gray)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
cv2.imwrite(now + '_2_' + file_name + '_otsu_C.jpg', binary)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
binary = cv2.dilate(binary, kernel)
cv2.imwrite(now + '_3_' + file_name + '_dilate_C.jpg', binary)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
target_contour = max(contours, key=lambda x: cv2.contourArea(x))
fig, ax = plt.subplots(figsize=(8, 8))
ax.imshow(img)
ax.set_axis_off()
for i, cnt in enumerate(contours):
if cv2.contourArea(cnt) < 500:
continue
else:
cnt = cnt.squeeze(axis=1)
ax.add_patch(Polygon(cnt, color="b", fill=None, lw=2))
ax.plot(cnt[:, 0], cnt[:, 1], "ro", mew=0, ms=4)
ax.text(cnt[0][0], cnt[0][1], i, color="orange", size="20")
plt.savefig(now + '_4_' + file_name + '_boundingbox_C.png')
plt.close()
buf_np = target_contour.squeeze(axis=1).flatten()
x_list = []
y_list = []
for i, elem in enumerate(buf_np):
if i%2==0:
x_list.append(elem)
else:
y_list.append(elem*(-1))
x_df = pd.Series(x_list)
y_df = pd.Series(y_list)
DF = pd.concat((x_df.rename(r'#X'), y_df.rename('Y')), axis=1, sort=False)
DF.to_csv(now + '_' + file_name + "_target_contour_B.csv", encoding="utf-8", index=False)
def draw_contours_D(file_name, img):
edges = cv2.Canny(img, 100, 200)
cv2.imwrite(now + '_' + file_name + '_boundingbox_D.jpg', edges)
def plot_scatter(myfile):
file_name = myfile[:-4]
df = pd.read_csv(myfile, header=0)
myX = df.iloc[:, 0].values.tolist()
myY = df.iloc[:, 1].values.tolist()
ax = plt.figure(num=0, dpi=120).gca()
ax.set_title(file_name, fontsize=14)
ax.set_xlabel('X Axis', fontsize=16)
ax.set_ylabel('Y Axis', fontsize=16)
ax.scatter(myX, myY, s=20, color="red")
ax.plot(myX, myY)
ax.set_aspect('equal', adjustable='box')
plt.grid(True)
plt.savefig(now + '_' + file_name + "_contours.png")
plt.close()
def main(my_file):
file_name = my_file[:-4]
img = cv2.imread(my_file)
draw_contours_A(file_name, img)
draw_contours_B(file_name, img)
draw_contours_C(file_name, img)
draw_contours_D(file_name, img)
if __name__ == '__main__':
my_jpg_list = glob.glob("*.jpg")
for my_file in my_jpg_list:
try:
main(my_file)
except:
print('somthing error')
my_csv_list = glob.glob("*.csv")
for my_file in my_csv_list:
try:
plot_scatter(my_file)
except:
print('somthing error')
以上
<広告>
リンク
リンク