前回はBPartISモデル利用して、粒子画像のインスタンスセグメンテーションを実行した。
続いて今回は、セグメンテーション画像から各粒子の特徴量として以下を計算する。
・面積
・周囲長
・最小外接短径の短辺、長辺、傾き
・楕円近似の短軸、長軸、傾き
・最小外接円の半径
各種特徴量の計算にはopencvライブラリを使用しており、下記のページを参考にしている。
輪郭: 初めの一歩 — OpenCV-Python Tutorials 1 documentation
領域(輪郭)の特徴 — OpenCV-Python Tutorials 1 documentation
ディレクトリ構成
ディレクトリ構成は以下の通り。この記事で新規に作成したものはmeasurement.pyのみで、コードの詳細はページ下部にまとめた。
.
└── bpartis
├── models
| └seg-model.pt
└── segment
├── model.py
├── nnmodules.py
├── cluster.py
├── uncertainty.py
├── visualization.py
└── measurement.py
その他のコードは、下記記事にまとめている。
実際に動かしてみた
ライブラリをインポート。
import cv2
import matplotlib.pyplot as plt
from bpartis.segment.measurement import Measurer
テスト用画像(test2imageモデルで生成した画像)、セグメンテーション画像の読み込み。
image = cv2.imread('./test.png')
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()
segmentation = cv2.imread('./results/test_seg.png', cv2.IMREAD_GRAYSCALE)
plt.imshow(segmentation)
plt.show()
ここで、セグメンテーション画像は2次元のndarrayで、背景は0、各粒子の画素にはインスタンスのidが画素値となっている。
特徴量計算用のクラスで、特徴量を算出して表示。
measurer = Measurer(segmentation, image)
print(measurer.params)
ここで、特徴量名と詳細は以下の通り。
特徴量名 | 詳細 | 特徴量名 | 詳細 |
area | 面積 | ellipse_short | 楕円近似の短軸 |
perimeter | 周囲長 | ellipse_long | 楕円近似の長軸 |
rect_short | 最小外接短形の短辺 | ellipse_angle | 楕円近似の傾き |
rect_long | 最小外接短形の長辺 | circle_r | 最小外接円の半径 |
rect_angle | 最小外接短形の傾き |
粒子の輪郭を図示する。
plt.imshow(measurer.draw_contours())
plt.show()
さらに粒子番号を画像に付番する。
img = measurer.draw_contours()
plt.imshow(measurer.draw_num(img))
plt.show()
その他も同様に図示できる。
# 最小外接短形
plt.imshow(measurer.draw_rect())
plt.show()
# 最小外接円
plt.imshow(measurer.draw_circle())
plt.show()
# 楕円近似
plt.imshow(measurer.draw_ellipse())
plt.show()
コード詳細
githubはこちら
GitHub - shashashanki/bpartis_test
Contribute to shashashanki/bpartis_test development by creating an account on GitHub.
import numpy as np
import pandas as pd
import cv2
class Measurer:
def __init__(self, segmentation, image=[]):
self.segmentation = segmentation
if image!=[]:
self.image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
else:
self.image = image
# unit8に変換しないとcv2で処理できない
self.contours, self.hierrarchy = cv2.findContours(segmentation.astype('uint8'),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
self.rects = []
self.circles = []
self.ellipses = []
self.params = pd.DataFrame(self.calc_param())
self.params.index += 1
def calc_param(self):
param_li = []
for cnt in self.contours:
# 面積
area = cv2.contourArea(cnt)
# 周囲長
perimeter = cv2.arcLength(cnt,True)
# 最小外接短形
rect = cv2.minAreaRect(cnt)
rect_l = [rect[1][0],rect[1][1]]
rect_l.sort()
# 最小外接円
circle = cv2.minEnclosingCircle(cnt)
# 楕円近似
ellipse = cv2.fitEllipse(cnt)
ellipse_r = [ellipse[1][0],ellipse[1][1]]
ellipse_r.sort()
param = {
'area':area,
'perimeter':perimeter,
'rect_short':rect_l[0],
'rect_long':rect_l[1],
'rect_angle':rect[2],
'ellipse_short':ellipse_r[0],
'ellipse_long':ellipse_r[1],
'ellipse_angle':ellipse[2],
'circle_r':circle[1],
}
param_li.append(param)
self.rects.append(rect)
self.circles.append(circle)
self.ellipses.append(ellipse)
return param_li
def draw_contours(self):
img = self.image.copy()
img = cv2.drawContours(img, self.contours, -1, (0,255,0), 2)
return img
def draw_rect(self):
img = self.image.copy()
for rect in self.rects:
box = cv2.boxPoints(rect)
box = np.int0(box)
img = cv2.drawContours(img,[box],0,(0,255,0),2)
return img
def draw_circle(self):
img = self.image.copy()
for circle in self.circles:
(x,y),radius = circle
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
return img
def draw_ellipse(self):
img = self.image.copy()
for ellipse in self.ellipses:
img = cv2.ellipse(img,ellipse,(0,255,0),2)
return img
def draw_num(self, img_):
img = img_.copy()
for i,rect in enumerate(self.rects):
x,y = int(rect[0][0]),int(rect[0][1])
img = cv2.putText(
img,
text=f'{i+1}',
org=(x,y),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=1.0,
color=(0,0,255),
thickness=2,
)
return img
コメント