本記事では、画像認識により図形の輪郭を検出して、各領域の面積を求める雛形コードを載せました。同時に、各領域の重心座標と輪郭の長さも数値データとしてcsvファイルに保存し、ヒストグラムや累積分布図も作成する仕様です。
各領域の検出と面積を求める過程を動画にしたのが下記リンク先です。
下図は、元画像です。
本プログラム中では画像ファイル名「'yyy_picture.jpg'」で指定しています。
下図は、本プログラム実行後の図です。
検出した領域を赤線で囲み、面積を各領域の重心位置に記載しています。
本プログラムを実行して得られた数値データは、下図のようにcsvファイルに保存します。B列のIDは領域名、C列のAreaは面積、D列とE列は領域の重心(x,y)、F列は輪郭の長さ、Gは画像面積全体を100%とした場合の各領域が占める割合を表します。
下図のようにヒストグラムを作成します。
下図のように累積分布図も作成します。
■本プログラム
#!/usr/bin/env python # coding: utf-8 # In[1]: import os import cv2 import numpy as np import scipy.ndimage as ndimage import matplotlib.pyplot as plt import matplotlib.ticker as ticker plt.rcParams["font.size"] = 20 import pandas as pd file_path = 'yyy_picture.jpg' basename = os.path.splitext(os.path.basename(file_path))[0] # In[2]: ### 画像読み込み img = cv2.imread(file_path, 1) # 画像の高さと幅を取得 h, w, c = img.shape # 拡大(拡大することで輪郭がぼやける。このぼやけにより境界を識別しやすくする) scale = 3 img_resize = cv2.resize(img, (w * scale, h * scale)) ### 画像処理 # グレースケールに変換 img_gray = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY) # ガウシアンによるスムージング処理(ぼかし) img_blur = cv2.GaussianBlur(img_gray, (5,5), 0) # 二値化と大津処理 r, dst = cv2.threshold(img_blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # モルフォロジー膨張処理 kernel = np.ones((3,3), np.uint8) dst = cv2.dilate(dst, kernel, iterations = 1) # 画像ファイルに保存 cv2.imwrite(basename + '_thresholds.jpg', dst) # In[3]: # もし画像欠けがあった場合に塗りつぶす処理 dst_fill = ndimage.binary_fill_holes(dst).astype(int) * 255 cv2.imwrite(basename + '_thresholds_fill.jpg', dst_fill) # In[4]: # 境界を検出して描画する contours, _ = cv2.findContours(dst_fill.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img_contour = cv2.drawContours(img_resize, contours, -1, (0,0,255), 1) cv2.imwrite(basename + '_counter.jpg', img_contour) # In[5]: # 面積、重心、輪郭長さを抽出する Areas = [] with open(basename + '_data.csv', 'w') as f: for i, contour in enumerate(contours): # 面積 area = cv2.contourArea(contour) area = area / 1000 Areas.append(area) # 輪郭の重心 M = cv2.moments(contour) cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) #輪郭(境界)の長さ perimeter = cv2.arcLength(contours[i],True) # 画像に出力する #img2 = img_resize.copy() img2 = cv2.drawContours(img_resize, contours, i, (0, 0, 255), 3) cv2.putText(img2, str('{:.1f}'.format(area)), (cx, cy), cv2.FONT_HERSHEY_PLAIN, # フォントタイプ 3, # 文字サイズ (0, 0, 0), # 文字色:白(255, 255, 255) 黒(0, 0, 0) 2, # 文字太さ cv2.LINE_AA) if i == (len(contours)-1): img2_resize = cv2.resize(img2, (w, h)) cv2.imwrite(basename + '_' + str(i) + '.jpg', img2_resize) # csvファイルに保存 if i == 0: my_columns_list = ['ID', 'Area', 'x_center_of_gravity', 'y_center_of_gravity', 'Perimeter'] my_columns_str = ','.join(my_columns_list) f.write(my_columns_str + '\n') else: my_data_list = [str(i), str(area), str(cx), str(cy), str(perimeter)] my_data_str = ','.join(my_data_list) f.write(my_data_str + '\n') Area_sum = sum(Areas) print('Area_sum', Area_sum) # In[6]: # ヒストグラム表示 fig = plt.figure(figsize=(8,6)) plt.title("histogram") plt.xlabel('Area') plt.ylabel('frequency') plt.tick_params() plt.grid() plt.hist(Areas, bins=10, rwidth=0.9) # binsは区分数 plt.savefig(basename + '_histogram.jpg') plt.close() # In[7]: # 面積を割合で出力する df = pd.read_csv(basename + '_data.csv') df[r'Area[%]'] = df['Area'] / Area_sum * 100 df.to_csv(basename + '_data_2.csv') df # In[8]: ### 累積分布図を表示する # ヒストグラムデータを抽出 values, base = np.histogram(df['Area'], bins=40) # binsは区分数 # 要素を足し合わせて、numpyアレイで出力する y = np.cumsum(values) # グラフをプロット fig = plt.figure(figsize=(8,6)) plt.plot(base[:-1], y, color='black', linewidth = 4) # 以下、グラフオプション plt.xlabel('Area') plt.ylabel('p') #plt.ylim(0, 1) # 目盛り表記を強制的に整数にする plt.gca().get_yaxis().set_major_locator(ticker.MaxNLocator(integer=True)) plt.grid() plt.title('Cumulative distribution') plt.savefig(basename + '_cumulative_distribution.jpg') plt.close() # In[ ]:
以上
<広告>
リンク