Python_(2/4)384ウェルの画像を、エクセルでリストを保管できるまでに | バイオとDX

バイオとDX

バイオ医薬品とDX、データサイエンス

# ===============================================
# 周囲をトリミングしたウェルの画像をリサイズ(960x640)
# ===============================================

#(1)あらかじめ画像はウェルだけにトリミングする
#(2)リサイズされた画像は960x640で保存される

#import
import cv2
import numpy as np
import os
import openpyxl
#import matplotlib.pyplot as plt

# ワークブック作成とシート指定
wb = openpyxl.Workbook()
ws = wb['Sheet']

# xyの空リスト用意
xy_list = []
area_list = []

#画像ファイル名入力
fname = input('input file name to be read ,as ###.jpg:')

#日付入力/ファイル名のため
date1 = input('date as yymmdd?:')

#ウェルID入力/ファイル名のため
wel_id = input('wel_id_as ###?:')

#画像読み込み
img = cv2.imread(fname)

if img is None:
    print ('ファイルを読み込めません。')
    import sys
    sys.exit()

#画像リサイズ
height = img.shape[0]
width = img.shape[1]

dst = cv2.resize(img, (960, 640))

# ===============================================
# 384ウェルのウェル1つずつ抽出、保存
# ===============================================

#(個別)画像ファイル保存フォルダ作成
# OpenCVのimwriteで保存先ディレクトリを指定
# https://jd-engineer.hateblo.jp/entry/2017/02/24/202747
dirname = date1+wel_id
if not os.path.exists(dirname):
    os.mkdir(dirname)

#ウェルの番地準備/ファイル名のため/文字表記
y_letter = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P']
x_letter = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24']

for y1 in range(0, 16, 1):
    for x1 in range(0, 24, 1):

#ウェルの番地設定/IDの文字列化/アドレスのためx40
        y2 = y1*40
        x2 = x1*40

#保存した(個別)画像ファイルの特定
        dst2 = dst[y2:y2+40, x2:x2+40]

#(個別)画像ファイルの保存
# OpenCVのimwriteで保存先ディレクトリを指定
# https://jd-engineer.hateblo.jp/entry/2017/02/24/202747
        name1 = date1+'_'+wel_id+'_'+y_letter[y1]+x_letter[x1]
        name2 = y_letter[y1]+x_letter[x1]
        
#画像書き込み0
#        cv2.imwrite(os.path.join(dirname, f'({name1}.jpeg'), dst2)

# ===============================================
# 個別画像をトリミング
# ===============================================

#画像リサイズ/#120x120でリサイズ
        height = dst2.shape[0]
        width = dst2.shape[1]
        dst3 = cv2.resize(dst2, (120, 120))

#画像書き込み1
#       cv2.imwrite(os.path.join(dirname, f'({name1}_120.jpeg'), dst3)

#上下左右トリミング/#15-105x15-105で上下左右トリミング
        dst4 = dst3[15:105, 15:105]

#画像書き込み2
#       cv2.imwrite(os.path.join(dirname, f'({name1}_90.jpeg'), dst4)

#四隅トリミング/中心45:45で横100縦125の円、(255,255,0)は線の色、thickness-1で塗りつぶし円でマスク
        mask = np.zeros(dst4.shape, dtype=np.uint8)
        cv2.ellipse(mask,((45, 45),(100, 125),0),(255,255,0),-1)
        dst5 = dst4 & mask

#画像書き込み3
#       cv2.imwrite(os.path.join(dirname, f'({name1}_90_mask.jpeg'), dst5)

# ===============================================
# ノイズ除去/GaussianBlur
# ===============================================

#画像img_gray#(2)GaussianBlurを用いたノイズ除去
#第二引数pixelは平均化するピクセル数で、1x1の場合は計1ピクセル
        pixel = 1
        img_gaus = cv2.GaussianBlur(dst5,(pixel,pixel),2)

# ===============================================
# 二値化
# ===============================================

#二値化する
#二値化する際の輝度閾値
        thresh_value=120
        thresh, dst6 = cv2.threshold(img_gaus, thresh_value, 255, cv2.THRESH_BINARY)

#画像書き込み4
#       cv2.imwrite(os.path.join(dirname, f'({name1}_gaus.jpeg'), dst6)

# ===============================================
# 輪郭を抽出して面積を計算
# ===============================================

#グレイスケール化して二値化
        dst7 = cv2.cvtColor(dst6,cv2.COLOR_BGR2GRAY)
        ret,thresh = cv2.threshold(dst7,120,255,0)
        
#輪郭を抽出 ※輪郭を検出する順番は、輪郭抽出の開始点の座標のyの値が大きい順に検出される
# https://imagingsolution.net/program/python/opencv-python/opencv-python-findcontours/
# https://qiita.com/bianca26neve/items/9f15e2a7856da01592b9
# https://pystyle.info/opencv-structural-analysis-and-shape-descriptors/
        contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 

#輪郭抽出ノイズの判断基準 contour_noise:3 ※輪郭この数値以上のみリスト化
        contour_noise =3

#面積が閾値未満の輪郭の削除する
        contours2 = list(filter(lambda x: cv2.contourArea(x) >= contour_noise, contours))

# 一番面積が大きい輪郭を選択/輪郭の数が1の時だけ採用、1以外(ゼロか複数)なら不採用
        if(len(contours2) == 1):
            cnt = max(contours2, key=lambda x: cv2.contourArea(x))

#輪郭抽面積の判断基準 area_min:200 ※輪郭内の面積がこれ未満なら不採用
#輪郭抽面積の判断基準 area_max:200 ※輪郭内の面積がこれ超過なら不採用
            area_min = 200
            area_max = 3000

#輪郭の面積を計算/findContours() で抽出された輪郭から cv2.contourArea で
            area = cv2.contourArea(cnt)
            if area > area_min and area < area_max:

# 適合するxyをリストに追加 ←For分の中にいれてxyリストを完成させる
                xy_list.append(name2)
                area_list.append(area)
                
#画像書き込み5
                cv2.imwrite(os.path.join(dirname, f'{area:05.0f}_{name1}.jpeg'), dst2)
                cv2.imwrite(os.path.join(dirname, f'{area:05.0f}_{name1}_bn.jpeg'), thresh)

#cv2終了作業
cv2.waitKey(0)
cv2.destroyAllWindows()

# xyのリスト分繰り返して「A2」からエクセルに入力 ←xy_listさえできてれば最後でOK
for i, xy in enumerate(xy_list):
    ws.cell(1,1).value = 'wel ID'
    ws.cell(1,2).value = 'wel address'
    ws.cell(1,3).value = 'area'
    ws.cell(i+2,1).value = wel_id
    ws.cell(i+2,2).value = xy_list[i]
    ws.cell(i+2,3).value = area_list[i]
    
# エクセルの保管
wb.save('wel_address.xlsx')
wb.close